Fix user data persistence across reboots for native Linux installs - #61
Conversation
- Add is_running_in_container() function to detect Docker/Podman environments - Add get_persistent_data_dir() function to determine where to store persistent data - On native Linux: use /var/lib/openplc-runtime for .env and database files - On containers: continue using /var/run/runtime (mounted as persistent volume) - Update installer to create /var/lib/openplc-runtime when systemd is available This fixes the issue where user credentials were lost after reboot because /var/run/runtime is a tmpfs that gets cleared on reboot. The .env file regeneration would then delete the database since new secrets invalidate old password hashes. Container detection checks: - /.dockerenv file existence - container/DOCKER_CONTAINER environment variables - /proc/1/cgroup for docker/kubepods/lxc/containerd patterns - /proc/1/environ for container= variable Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes user credential loss after system reboots on native Linux installations. The issue occurred because persistent data (.env and restapi.db) were stored in /var/run/runtime, a tmpfs directory cleared on reboot, causing password hash invalidation and database regeneration.
Key changes:
- Introduced container detection logic to differentiate Docker from native Linux environments
- Routes persistent data to
/var/lib/openplc-runtimeon native Linux while preserving/var/run/runtimefor Docker deployments - Modified installer to create the persistent data directory on systemd-based systems
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| webserver/config.py | Added container detection function and separate persistent data directory logic to route .env and database files appropriately |
| install.sh | Creates /var/lib/openplc-runtime directory on native Linux systems with systemd support |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| cgroup_content = f.read() | ||
| if "docker" in cgroup_content or "kubepods" in cgroup_content: | ||
| return True | ||
| # Also check for containerd/cri-o patterns | ||
| if "/lxc/" in cgroup_content or "containerd" in cgroup_content: | ||
| return True |
There was a problem hiding this comment.
Reading the entire cgroup file and performing multiple substring searches is inefficient. Consider checking for all patterns in a single pass or stopping at the first match to avoid unnecessary string operations.
| cgroup_content = f.read() | |
| if "docker" in cgroup_content or "kubepods" in cgroup_content: | |
| return True | |
| # Also check for containerd/cri-o patterns | |
| if "/lxc/" in cgroup_content or "containerd" in cgroup_content: | |
| return True | |
| indicators = ("docker", "kubepods", "/lxc/", "containerd") | |
| for line in f: | |
| if any(indicator in line for indicator in indicators): | |
| return True |
| with open("/proc/1/environ", "rb") as f: | ||
| environ_content = f.read().decode("utf-8", errors="ignore") | ||
| if "container=" in environ_content: | ||
| return True |
There was a problem hiding this comment.
Reading and decoding the entire /proc/1/environ file is inefficient. The file can be large and contain many null-separated environment variables. Consider reading in chunks or using a more targeted search approach.
| with open("/proc/1/environ", "rb") as f: | |
| environ_content = f.read().decode("utf-8", errors="ignore") | |
| if "container=" in environ_content: | |
| return True | |
| pattern = b"container=" | |
| tail_len = len(pattern) - 1 | |
| with open("/proc/1/environ", "rb") as f: | |
| leftover = b"" | |
| while True: | |
| chunk = f.read(4096) | |
| if not chunk: | |
| break | |
| data = leftover + chunk | |
| if pattern in data: | |
| return True | |
| if tail_len > 0: | |
| leftover = data[-tail_len:] |
| data_dir = Path("/var/lib/openplc-runtime") | ||
|
|
||
| # Ensure the directory exists with appropriate permissions | ||
| data_dir.mkdir(parents=True, exist_ok=True) |
There was a problem hiding this comment.
Creating the directory without explicit permission checks could lead to security issues if the directory is created with unexpected ownership or permissions. On native Linux, /var/lib/openplc-runtime should have restricted permissions (e.g., 0755) and appropriate ownership to prevent unauthorized access to sensitive data like .env files containing secrets.
| data_dir.mkdir(parents=True, exist_ok=True) | |
| if data_dir == Path("/var/lib/openplc-runtime"): | |
| # Explicitly set permissions for sensitive persistent data directory | |
| data_dir.mkdir(parents=True, mode=0o755, exist_ok=True) | |
| try: | |
| os.chmod(data_dir, 0o755) | |
| except PermissionError: | |
| logger.warning( | |
| "Could not set permissions on %s to 0755 due to insufficient privileges", | |
| data_dir, | |
| ) | |
| else: | |
| data_dir.mkdir(parents=True, exist_ok=True) |
Summary
Fixes an issue where user credentials were lost after reboot on native Linux installations. The root cause was that
/var/run/runtimeis a tmpfs that gets cleared on reboot. When the.envfile disappeared, a new one was generated with new secrets, which invalidated the existing password hashes and triggered database deletion.Solution: Store persistent data (
.envandrestapi.db) in/var/lib/openplc-runtimeon native Linux, while continuing to use/var/run/runtimefor Docker deployments (where it's expected to be a mounted persistent volume).Key changes:
is_running_in_container()function with multiple detection methods (/.dockerenv, env vars, cgroup, /proc/1/environ)get_persistent_data_dir()to route persistent data to the appropriate location based on environment/var/lib/openplc-runtimewhen systemd is availableReview & Testing Checklist for Human
-v openplc-runtime-data:/var/run/runtimeand confirm data persists in the mounted volume (not/var/lib/openplc-runtime)/var/lib/openplc-runtimehas appropriate permissions (755) and files inside have correct ownershipRecommended Test Plan
.envandrestapi.dbare in/var/lib/openplc-runtime/(not/var/run/runtime/)Notes
/var/run/runtimeRequested by: Thiago Alves (@thiagoralves)
Link to Devin run: https://app.devin.ai/sessions/ebc278ce265842bba5abf29df61e1201