Post

Icon Python Lambda Executor Security Architecture

Understanding how Krill securely executes user-provided Python scripts with multi-level sandboxing and isolation

Python Lambda Executor Security Architecture

Overview

Krill Server includes a powerful Lambda execution system that allows users to run custom Python scripts as part of their automation workflows. Since these scripts run on your server with access to your data, security is paramount. This document explains Krill’s multi-layered security approach to safely execute user-provided Python code.

What Are Krill Lambdas?

Lambdas in Krill are Python scripts that act as data transformation functions in your automation pipeline. They:

  • Read input from a source DataPoint (sensor, API, or other data source)
  • Process data using your custom Python logic
  • Write output to a target DataSource for storage or further processing

Common use cases include:

  • Data transformation (e.g., temperature unit conversion)
  • Sensor data parsing and averaging
  • Custom calculations and algorithms
  • Integration with external services

Lambda Execution Flow

sequenceDiagram
    participant Source as Source DataPoint
    participant Lambda as Lambda Node
    participant Executor as Python Executor
    participant Sandbox as Sandbox (Firejail/Docker)
    participant Script as Python Script
    participant Target as Target DataSource

    Source->>Lambda: New data arrives
    Lambda->>Executor: Trigger execution
    Executor->>Executor: Validate script path
    Note over Executor: Path traversal check<br/>Must be in /opt/krill/lambdas
    
    Executor->>Executor: Select sandbox type
    Note over Executor: Firejail > Docker > None
    
    Executor->>Sandbox: Start sandboxed process
    Note over Sandbox: Memory limit: 256MB<br/>Timeout: 30s<br/>Restricted filesystem
    
    Sandbox->>Script: Execute with input
    Script-->>Sandbox: Return output
    Sandbox-->>Executor: Capture stdout
    
    Executor->>Target: Store result
    Target-->>Source: Complete

Security Architecture

Defense in Depth Strategy

Krill implements multiple security layers to protect your system:

flowchart TD
    A[User Script] --> B[Layer 1: Path Validation]
    B --> C[Layer 2: Sandbox Detection]
    C --> D{Sandbox Available?}
    D -->|Yes| E[Layer 3: Isolated Execution]
    D -->|No| F[Unsandboxed Execution]
    E --> G[Layer 4: Resource Limits]
    F --> G
    G --> H[Layer 5: Timeout Protection]
    H --> I[Layer 6: Output Capture]
    I --> J[Safe Result Storage]
    
    style B fill:#90EE90
    style E fill:#90EE90
    style G fill:#90EE90
    style H fill:#90EE90
    style I fill:#90EE90
    style F fill:#FFB6C1

Layer 1: File System Isolation

Path Traversal Prevention

All lambda scripts must reside in /opt/krill/lambdas. The executor validates paths using canonical path resolution:

1
2
3
4
5
6
7
8
9
10
internal fun isPathWithinAllowedDirectory(file: File): Boolean {
    return try {
        val canonicalRoot = File(sandboxConfig.allowedPath).canonicalPath
        val canonicalFile = file.canonicalPath
        canonicalFile.startsWith(canonicalRoot)
    } catch (e: IOException) {
        logger.e("Error validating path: ${e.message}", e)
        false
    }
}

Why this matters:

  • Prevents ../ path traversal attacks
  • Blocks access to system directories
  • Ensures scripts only run from trusted location

Layer 2: Sandbox Detection

Krill automatically detects available sandboxing mechanisms at startup:

  1. Firejail (Preferred) - Lightweight Linux namespace sandboxing
  2. Docker (Fallback) - Container-based isolation
  3. None (Last resort) - Direct execution with logging warning
1
2
3
4
5
6
7
8
9
10
11
12
private fun detectSandboxType(): SandboxType {
    if (isCommandAvailable("firejail")) {
        logger.i { "Sandbox: firejail detected and will be used" }
        return SandboxType.FIREJAIL
    }
    if (isCommandAvailable("docker")) {
        logger.i { "Sandbox: docker detected and will be used" }
        return SandboxType.DOCKER
    }
    logger.w { "Sandbox: No mechanism available. Scripts will run without isolation." }
    return SandboxType.NONE
}

When Firejail is available, Krill uses it for lightweight, effective sandboxing:

Filesystem Restrictions:

1
2
3
4
5
firejail \
  --whitelist=/opt/krill/lambdas \
  --read-only=/opt/krill/lambdas \
  --private \
  --private-tmp
  • Scripts can only read files in /opt/krill/lambdas
  • Lambda directory is mounted read-only
  • Scripts get isolated /tmp and home directories
  • No access to /home, /root, or other user directories

Resource Limits:

1
2
--rlimit-as=268435456    # 256MB memory limit
--timeout=30:30          # 30 second execution timeout

Optional Network Isolation:

1
--net=none  # When restrictNetwork = true

Installing Firejail on Debian/Ubuntu:

1
2
sudo apt-get update
sudo apt-get install firejail

Layer 4: Docker Sandboxing (Alternative)

When Docker is available but Firejail isn’t, Krill uses containerization:

1
2
3
4
5
6
7
8
9
10
11
docker run \
  --rm \
  --memory=256m \
  --memory-swap=256m \
  --cpu-period=100000 \
  --cpu-quota=30000000 \
  --read-only \
  -v /opt/krill/lambdas:/opt/krill/lambdas:ro \
  -w /opt/krill/lambdas \
  python:3-slim \
  python3 /opt/krill/lambdas/script.py input

Security features:

  • Memory hard limit (256MB RAM, no swap)
  • CPU quota enforcement
  • Read-only root filesystem
  • Lambda directory mounted read-only
  • Minimal Python image (no unnecessary tools)
  • Optional network isolation (--network=none)

Layer 5: Resource Limits

All script executions are protected by hard limits:

ResourceLimitPurpose
Memory256MBPrevent memory exhaustion
Execution Time30 secondsPrevent infinite loops
FilesystemRead-only /opt/krill/lambdasPrevent file system tampering
Process Count1 (implicit)Prevent fork bombs

Layer 6: User and Permission Isolation

System User Setup (from postinst):

1
2
3
4
5
6
# Create krill system user
adduser --system --ingroup krill --no-create-home krill

# Set lambda directory ownership
chown -R krill:krill /opt/krill/lambdas
find /opt/krill/lambdas -type f -name "*.py" -exec chmod 755 {} \;

Key security properties:

  • Scripts run as krill user (not root)
  • krill user has no home directory
  • krill user has no login shell
  • Limited group memberships (only hardware access groups when needed)

Installation and Setup

For Debian Package Users

When you install Krill via .deb package:

  1. System user created automatically:
    1
    2
    
    # Handled by postinst
    adduser --system --ingroup krill --no-create-home krill
    
  2. Lambda directory initialized:
    1
    2
    3
    4
    
    /opt/krill/lambdas/
    ├── KrillPythonLambdaBasic.py       (example: echo input)
    ├── TemperatureConverter.py         (example: C to F conversion)
    └── sht30.py                         (example: sensor data parser)
    
  3. Permissions set securely:
    • Directory: 0750 (rwxr-x—)
    • Scripts: 0755 (rwxr-xr-x)
    • Owner: krill:krill

For maximum security, install Firejail:

1
2
3
4
5
6
7
8
9
10
# Debian/Ubuntu/Raspberry Pi OS
sudo apt-get update
sudo apt-get install firejail

# Verify installation
which firejail
firejail --version

# Restart Krill to detect firejail
sudo systemctl restart krill

Check logs to confirm:

1
2
sudo journalctl -u krill -f | grep Sandbox
# Should see: "Sandbox: firejail detected and will be used"

Installing Docker (Alternative)

If you prefer Docker-based sandboxing:

1
2
3
4
5
6
7
8
9
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Add krill user to docker group
sudo usermod -aG docker krill

# Restart Krill
sudo systemctl restart krill

Adding Your Own Lambda Scripts

Safe Script Development

1. Create your script in the lambda directory:

1
sudo nano /opt/krill/lambdas/my_script.py

2. Follow the template pattern:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env python3
"""
My Custom Lambda
Description of what this script does
"""

import sys
import json

def main():
    # Validate input exists
    if len(sys.argv) < 2:
        print("ERROR: No input provided", file=sys.stderr)
        sys.exit(1)
    
    try:
        # Parse input
        input_value = sys.argv[1]
        
        # Your processing logic here
        result = process_data(input_value)
        
        # Output result to stdout
        print(result)
        
    except Exception as e:
        print(f"ERROR: {e}", file=sys.stderr)
        sys.exit(1)

def process_data(input_val):
    # Your custom logic
    return input_val.upper()

if __name__ == "__main__":
    main()

3. Set correct permissions:

1
2
sudo chown krill:krill /opt/krill/lambdas/my_script.py
sudo chmod 755 /opt/krill/lambdas/my_script.py

4. Test your script manually:

1
2
3
4
5
6
7
8
9
10
11
# Test as krill user
sudo -u krill python3 /opt/krill/lambdas/my_script.py "test input"

# Test in sandbox (if firejail installed)
sudo -u krill firejail \
  --noprofile \
  --whitelist=/opt/krill/lambdas \
  --read-only=/opt/krill/lambdas \
  --private \
  --private-tmp \
  python3 /opt/krill/lambdas/my_script.py "test input"

Security Best Practices for Lambda Scripts

DO:

  • Keep scripts simple and focused
  • Validate all inputs
  • Use try-catch for error handling
  • Write errors to stderr, results to stdout
  • Test scripts manually before deploying
  • Use standard library when possible

DON’T:

  • Import unnecessary external libraries
  • Attempt to modify files
  • Spawn subprocesses
  • Make network requests (unless explicitly needed)
  • Store sensitive credentials in scripts
  • Assume unbounded execution time

Example Scripts

Basic Echo (Data Pass-through):

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3
import sys

def main():
    if len(sys.argv) < 2:
        print("ERROR: No input provided", file=sys.stderr)
        sys.exit(1)
    
    print(sys.argv[1])

if __name__ == "__main__":
    main()

Temperature Conversion:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python3
import sys

def celsius_to_fahrenheit(celsius):
    return (celsius * 9/5) + 32

def main():
    if len(sys.argv) < 2:
        print("ERROR: No input provided", file=sys.stderr)
        sys.exit(1)
    
    try:
        celsius = float(sys.argv[1])
        fahrenheit = celsius_to_fahrenheit(celsius)
        print(f"{fahrenheit:.2f}")
    except ValueError:
        print(f"ERROR: Invalid number: {sys.argv[1]}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()

Sensor Data Averaging:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python3
import sys
import json

def main():
    if len(sys.argv) < 2:
        print("ERROR: No input provided", file=sys.stderr)
        sys.exit(1)
    
    try:
        # Parse JSON array of values
        data = json.loads(sys.argv[1])
        values = [float(x) for x in data]
        average = sum(values) / len(values)
        print(f"{average:.2f}")
    except (json.JSONDecodeError, ValueError, ZeroDivisionError) as e:
        print(f"ERROR: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()

Configuration in Krill UI

Once your script is in /opt/krill/lambdas/, configure it in the Krill interface:

  1. Create a Lambda Node
  2. Configure:
    • Filename: my_script.py (just the filename, not full path)
    • Source: Select the DataPoint that triggers execution
    • Target: Select the DataSource to store results
  3. Test: Update the source DataPoint and verify target receives output

Current Security Limitations

While Krill’s sandboxing is robust for typical use cases, be aware:

⚠️ Not production-grade isolation:

  • Firejail uses Linux namespaces, not full virtualization
  • Docker containers share the kernel with host
  • Scripts run as krill user (not fully unprivileged)

⚠️ Resource limits are soft in unsandboxed mode:

  • Without Firejail/Docker, only timeout is enforced
  • Memory limits require sandbox support

⚠️ Network access:

  • Network isolation is optional (restrictNetwork = false by default)
  • Scripts can make outbound connections unless restricted
  • This is intentional for IoT/API integration use cases

Security Roadmap

Future improvements planned for enhanced security:

Short Term

  • Script signature verification - Optional code signing for trusted environments
  • Execution audit logging - Log all script executions with inputs/outputs
  • Per-lambda rate limiting - Prevent execution flooding
  • Enhanced Docker security - Add --security-opt=no-new-privileges, --cap-drop=ALL

Medium Term

  • CPU limit configuration - Per-script CPU quota controls
  • Disk I/O rate limiting - Prevent I/O abuse
  • Output size limits - Cap maximum stdout/stderr size
  • Environment variable restrictions - Whitelist approach for env vars

Long Term

  • Seccomp filtering - System call whitelisting at kernel level
  • gVisor runtime option - User-space kernel for stronger isolation
  • WebAssembly lambda support - True sandboxing via WASM runtime
  • Built-in lambda library - Curated, pre-audited transformation functions

Threat Model

What Krill Protects Against

Path traversal attacks - Scripts cannot escape /opt/krill/lambdas
Memory exhaustion - 256MB hard limit (with sandbox)
Infinite loops - 30-second timeout
Filesystem tampering - Read-only lambda directory
Privilege escalation - Scripts run as unprivileged krill user
Fork bombs - Single process limit (sandboxed)

What Users Are Responsible For

🔒 Script content trust - Only add scripts you understand and trust
🔒 Dependency auditing - Review any pip install requirements
🔒 Access control - Secure SSH/physical access to server
🔒 Network security - Firewall and network segmentation
🔒 System updates - Keep Debian, Python, and Krill updated

Known Attack Vectors (Mitigated)

AttackMitigationStatus
Path traversalCanonical path validation✅ Blocked
Resource exhaustionMemory/time limits✅ Limited (sandbox required)
Filesystem writesRead-only mounts✅ Blocked (sandboxed)
Network abuseOptional --net=none⚠️ User configurable
Privilege escalationUnprivileged user✅ Mitigated
Code injectionNo eval/exec in framework✅ Not possible

Monitoring and Troubleshooting

Check Sandbox Status

1
2
3
4
5
6
7
8
# View Krill logs
sudo journalctl -u krill -f

# Look for sandbox detection messages
sudo journalctl -u krill | grep Sandbox

# Expected output (with firejail):
# "Sandbox: firejail detected and will be used for script isolation"

Debug Script Execution

1
2
3
4
5
6
7
8
# View all lambda execution attempts
sudo journalctl -u krill | grep "Executing Python script"

# View script failures
sudo journalctl -u krill | grep "Python script failed"

# View script timeouts
sudo journalctl -u krill | grep "timed out"

Manual Testing

1
2
3
4
5
6
7
8
9
10
11
12
# Test script as krill user
sudo -u krill python3 /opt/krill/lambdas/my_script.py "test"

# Test with firejail sandbox
sudo -u krill firejail \
  --noprofile \
  --whitelist=/opt/krill/lambdas \
  --read-only=/opt/krill/lambdas \
  --private \
  --private-tmp \
  --rlimit-as=268435456 \
  python3 /opt/krill/lambdas/my_script.py "test"

Common Issues

Script not found:

1
ERROR: File not found: /opt/krill/lambdas/missing.py

→ Verify script exists and has correct permissions

Path validation failed:

1
ERROR: Security violation: Script path is outside allowed directory

→ Script must be in /opt/krill/lambdas/, check for symlinks

Timeout:

1
ERROR: Python script timed out after 30s

→ Optimize script or break into smaller operations

Memory limit (sandboxed):

1
Process terminated (OOM)

→ Reduce memory usage or consider chunking data

Comparison with Other Lambda Systems

FeatureKrillAWS LambdaAzure Functions
DeploymentLocal serverCloudCloud
IsolationFirejail/DockerFirecrackerHyper-V
Cold start~100ms~1s~1-3s
Memory limit256MB128MB-10GB128MB-4GB
Timeout30s15min5-10min
CostFreePay per executionPay per execution
Use caseIoT/EdgeWeb/APIEnterprise

Krill is designed for edge computing and IoT automation, prioritizing:

  • Low latency (no network round-trip)
  • Local data processing
  • Offline operation
  • Simple deployment

Conclusion

Krill’s Python Lambda executor provides a secure, flexible way to extend your automation capabilities while protecting your system. The multi-layered security approach ensures that user scripts run in a controlled environment, preventing common attack vectors.

Key takeaways:

  • ✅ Install Firejail for best security (apt-get install firejail)
  • ✅ Only add scripts you trust and understand
  • ✅ Test scripts manually before deploying
  • ✅ Monitor logs for execution failures
  • ✅ Keep your system and dependencies updated

For questions or security concerns, please open an issue on GitHub or contact the development team.

Additional Resources


Last updated: December 28, 2025
Krill version: 1.0+
Platform: Debian/Ubuntu/Raspberry Pi OS

This post is licensed under CC BY 4.0 by the author.