At this point it should just work, and if it doesn't work on Linux it is a bug; however, no benchmarking has been done and only the most cursory security analysis.
My favourite type of peas is HTT Ps
- ACME Integration: Built-in Let's Encrypt certificate management (no certbot required!)
- Automatic Domain Discovery: Scans
/etc/letsencrypt/live/
for domains or uses ACME - Dual Protocol Support: Serves both HTTP (port 80) and HTTPS (port 443)
- Static File Serving: Serves files from
/var/www/{domain}/
for each domain- Falls back to /var/www/html
- Let's Encrypt Integration: Uses fullchain.pem and privkey.pem certificates
- HTTP-01 Challenge Support: Handles ACME domain validation challenges
- Automatic Certificate Renewal: Background task for certificate management
- Extension System: Modular architecture with multiple extension types
- Comment System: Example extensions implementing commenting with moderation capabilities
- Admin Panels: Secure admin interfaces for content management
- Privilege Dropping: Drops to
www-data
after initialization for security - CGI-like Support: Executes statically compile and linked CGI-like scripts for dynamic content
- MIME Type Support: Proper content types for common file formats
- Comprehensive Logging: Detailed request and error logging
- Rust 1.70+
- Let's Encrypt certificates in
/etc/letsencrypt/live/
(or use ACME mode) - Document roots in
/var/www/{domain}/
- Root privileges to bind to port 443
EasyPeas features a powerful modular extension system with four types of extensions:
.expand.rs
- Content expansion extensions that modify HTML content.bin.rs
- CGI-bin like extensions for dynamic content generation.root.rs
- Root-level extensions that run before privilege dropping.admin.rs
- Admin panel extensions for content management
Drop these files into extensions/
at compile time to have you extensions linked into your single file webserver.
comment.expand.rs
: Adds comment forms and displays live commentscomment.bin.rs
: Handles comment submission via CGI-like APIcomment.root.rs
: Sets up comment directories and permissionscomment.admin.rs
: Provides comment moderation interface
- Converts
#EXPAND:math(op,i,j)
blocks to rendered math, where op can be e.g. "add"
- Demonstrates basic extension functionality
- Adds example content to pages
Extensions are automatically discovered by the build system. To create a new extension:
- Add a
.rs
file to theextensions/
directory with the appropriate suffix - Implement the required trait methods
- The build system will automatically compile and register your extension
// extensions/my_extension.expand.rs
use std::collections::HashMap;
pub fn extend(url: &str, args: &str) -> String {
// Your extension logic here
format!("<div>Custom content for {}</div>", url)
}
- Clone or download this project
- Build the server:
cargo build --release
- Deploy using the included script:
./deploy.sh user@your-server.com
-
Create document roots for your domains:
sudo mkdir -p /var/www/example.com echo "<h1>Hello from example.com!</h1>" | sudo tee /var/www/example.com/index.html
-
Run the server with ACME certificate management:
# Email defaults to webmaster@$HOSTNAME (if hostname contains a dot) or webmaster@domain (shortest domain from reverse DNS) or webmaster@localhost # Staging defaults to false (production Let's Encrypt) # Run with domain list sudo ./target/release/easypeas example.com another-domain.com
-
For testing, use the staging environment:
export ACME_STAGING="true" sudo ./target/release/easypeas example.com another-domain.com
-
Customize the email address:
export ACME_EMAIL="admin@example.com" sudo ./target/release/easypeas example.com another-domain.com
-
Ensure your Let's Encrypt certificates are in place:
/etc/letsencrypt/live/example.com/fullchain.pem /etc/letsencrypt/live/example.com/privkey.pem
-
Create document roots for your domains:
sudo mkdir -p /var/www/example.com echo "<h1>Hello from example.com!</h1>" | sudo tee /var/www/example.com/index.html
-
Run the server (requires root for port 443):
sudo ./target/release/easypeas
Or use the systemd service (if deployed):
sudo systemctl start easypeas sudo systemctl enable easypeas # Start on boot
-
Visit your domains:
- http://example.com (HTTP on port 80)
- https://example.com (HTTPS on port 443)
- http://another-domain.com
- https://another-domain.com
EasyPeas includes a complete commenting system with moderation capabilities:
- Comment Forms: Automatically replaces '#EXPAND:comment()' in served html files
- Live Comments: Accepted comments appear immediately on the page
- Moderation: Admin interface for approving/rejecting comments
- Security: Comments are sanitized and validated
- Storage: Comments stored in
/var/spool/easypeas/comments/
- Access via secret URL:
https://your-domain.com/comment_{admin_key}
- Admin key is generated automatically on first run and stored in /var/spool/easypeas.admin
- Batch moderation with checkboxes
EasyPeas provides secure admin panels for content management:
- Keys are generated dynamically on first run
- Stored in
/var/spool/easypeas/admin
- Keys are cached in memory for security
- Each extension gets its own unique admin key
- go to https://example.com/KEY to administer system.
- Admin keys are long, random alphanumeric strings
- Admin panels only accessible with correct keys
- Privilege dropping ensures admin operations run as
www-data
The server supports two modes of operation:
ACME Mode (Default when domains are specified):
- Automatically requests Let's Encrypt certificates for specified domains
- Handles HTTP-01 challenges for domain validation
- Stores certificates in
/var/lib/easypeas/certs/
- Automatically renews certificates before expiration
- Uses staging environment by default (set
ACME_STAGING=false
for production)
Legacy Mode (Fallback):
- Scans
/etc/letsencrypt/live/
for existing domains - Uses pre-existing certificates from certbot or other tools
- No automatic certificate management
ACME_EMAIL
: Email address for Let's Encrypt registration (defaults towebmaster@$HOSTNAME
if hostname contains a dot, otherwisewebmaster@domain
where domain is the shortest domain found by reverse DNS, orwebmaster@localhost
as final fallback)ACME_STAGING
: Set to "true" for staging Let's Encrypt environment (defaults to "false" for production)ENABLE_DNS_DISCOVERY
: Enable automatic hostname discovery via DNS (defaults to "true")
The server automatically:
- Serves files from
/var/www/{domain}/
for each domain - Uses the first domain found as the default domain
- Maps file extensions to appropriate MIME types
- Handles ACME HTTP-01 challenges at
/.well-known/acme-challenge/
- HTML:
.html
- CSS:
.css
- JavaScript:
.js
- JSON:
.json
- WASM:
.wasm
- Images:
.png
,.jpg
,.jpeg
,.gif
,.svg
,.ico
- Text:
.txt
- Default:
application/octet-stream
- This is a basic implementation for development/testing
- In production, consider additional security measures
- Ensure proper file permissions on document roots
- Consider rate limiting and access controls
The project includes test scripts for different platforms:
setup_example.sh
: Sets up example domains and content (Linux/macOS)test_server.sh
: Creates test environment with self-signed certificates (Linux/macOS)test_server.bat
: Windows batch script for test environment setuptest_server.ps1
: PowerShell script for test environment setup (Not sure that this would work on Windows though)
# Run the bash test script
chmod +x test_server.sh
./test_server.sh
#Test on a remote server
./remote_tesh.sh example.com
# Build and run the server
cargo build --release
sudo ./target/release/easypeas
Use the included deploy.sh
script for easy deployment:
./deploy.sh user@your-server.com
This script will:
- Build the release binary
- Copy it to the target server
- Install it to
/usr/local/bin/easypeas
- Create and enable a systemd service
- Set up proper security configurations
-
Build the binary:
cargo build --release
-
Copy to target server:
scp target/release/easypeas user@server:/usr/local/bin/
-
Set permissions:
ssh user@server "sudo chmod +x /usr/local/bin/easypeas"
-
Create systemd service (see
deploy.sh
for the service file)
The EasyPeas service includes:
- Automatic restart on failure
- Security hardening (NoNewPrivileges, PrivateTmp, etc.)
- Proper file system access controls
- Journal logging
Service management:
sudo systemctl start easypeas # Start service
sudo systemctl stop easypeas # Stop service
sudo systemctl restart easypeas # Restart service
sudo systemctl status easypeas # Check status
sudo journalctl -u easypeas -f # View logs
The project includes optimized build profiles:
- Development: Fast compilation with debug info
- Test: Balanced optimization for testing
- Release: Maximum optimization with LTO, size optimization, and stripped symbols
- Debug build: ~62 MB
- Release build: ~4.9 MB
The release profile uses:
lto = "fat"
: Full Link Time Optimizationcodegen-units = 1
: Single codegen unit for better optimizationopt-level = "z"
: Optimize for sizestrip = true
: Remove debug symbolspanic = "abort"
: Smaller binary size
EasyPeas_HTTPS/
├── src/ # Source code
│ ├── main.rs # Main server implementation
│ └── cgi_env.rs # CGI environment utilities
├── extensions/ # Extension modules
│ ├── comment.*.rs # Comment system extensions
│ ├── math.expand.rs # Math rendering extension
│ └── example.expand.rs # Example extension
├── target/ # Build output
│ └── release/easypeas # Compiled binary
├── deploy.sh # Deployment script
├── Cargo.toml # Rust project configuration
└── README.md # This file
/var/www/{domain}/ # Document roots for each domain
/etc/letsencrypt/live/ # Let's Encrypt certificates
/var/spool/easypeas/ # EasyPeas data directory
├── admin # Admin keys file
└── comments/ # Comment system storage
├── in # Incoming comments
├── processing # Comments awaiting moderation
├── accept # Accepted comments
├── reject # Rejected comments
└── live/ # Live comments by URL hash
-
Permission Denied on Port 443
- Ensure running as root or with sudo
- Check if another service is using port 443
-
Certificate Not Found
- For ACME mode: Ensure domains are specified as command line arguments
- For legacy mode: Verify certificates exist in
/etc/letsencrypt/live/{domain}/
- Check file permissions (should be readable by root)
- Ensure ACME_EMAIL environment variable is set for ACME mode
-
Admin Panel Not Accessible
- Check admin key in
/var/spool/easypeas/admin
- Verify URL format:
https://domain.com/extension_{key}
- Check admin key in
-
Comments Not Appearing
- Check comment moderation in admin panel
- Verify
/var/spool/easypeas/comments/
directory permissions - Ensure
www-data
user has write access
-
Extensions Not Loading
- Check build output for compilation errors
- Verify extension files are in
extensions/
directory - Ensure proper trait implementations
-
ACME Certificate Issues
- Verify domain is accessible from the internet
- Check that port 80 is open for HTTP-01 challenges
- Ensure ACME_EMAIL is set correctly
- Use staging environment first (
ACME_STAGING=true
) - Check certificate directory permissions:
/var/lib/easypeas/certs/
- Check server logs:
sudo journalctl -u easypeas -f
- Verify file permissions:
ls -la /var/spool/easypeas/comments/
- Test admin access:
curl -k https://domain.com/comment_{key}
- Check certificate validity:
openssl x509 -in /etc/letsencrypt/live/domain/fullchain.pem -text -noout
The easyp webserver is distributed under the GPLv3.
The library this was forked from was licensed under
- Apache License version 2.0.
- MIT license.
- ISC license.
The GPLv3 is liberal enough for what most normal people would want to do with a webserver, including most commericial purposes. If you want to distribute under a license other than GPLv3 feel free to drop me a line. Alternatively just use the permissively licensed upstream library at https://github.com/rustls/rustls
- Security Audit
- Supply security updates via some secure channel.
- Investigate feasibility of automatic free subdomain instead of self-signed cert fallback.