The reason why a dynamic DNS solution is needed is because you might use a nomadic contract, which mean your website is traveling from one node to another (A good video explanation can be found here: https://videos.files.wordpress.com/jeMNdrjU/replicator.mp4 )
To try this out right away, without doing anything, just deploy with this line:
evdevkit acquire -i everwordpress/everwordpress:latest rADDRESS -m 1
Standard database info:
user: wordpress
database name: wordpress
password: wordpress_password
Create a domain at a provider and generate ssl certificates for it, save them as private.pem and public.pem, activate the ssl plugin and use it to update your ssl certificates.
If you use a dynamic DNS, activate the evercurl plugin and use it to curl to your dynamic DNS provider.
Make sure your curl work before you set your domain, there's a log with curl responses.
There's a cron running in the backend which is sending wordpress heartbeats, so wordpress cron isn't needed.
I used this tutorial for my SSL and Domain:
https://evernode.forum/viewtopic.php?t=33
You set your domain up as Dynamic on the site, and then you get a curl command to use, which you use in your wordpress installation!
Files:
certwatcher.sh
Code: Select all
#!/bin/bash
UPLOAD_DIR="/var/www/ssl/uploads"
CERT_DIR="/var/www/ssl"
CERT_PATH="$CERT_DIR/public.pem"
KEY_PATH="$CERT_DIR/private.pem"
LOG_FILE="/var/www/server.log"
# Function to log messages
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}
# Function to validate the certificate and key
validate_certs() {
cert_file=$1
key_file=$2
# Check if the certificate and key match
cert_modulus=$(openssl x509 -noout -modulus -in "$cert_file" | openssl md5)
key_modulus=$(openssl rsa -noout -modulus -in "$key_file" | openssl md5)
if [ "$cert_modulus" == "$key_modulus" ]; then
log_message "Certificate and key match."
return 0 # Success
else
log_message "Error: Certificate and key do not match!"
return 1 # Failure
fi
}
# Watch for changes in the upload directory
while true; do
# Wait for file creation or modification in the upload directory
inotifywait -e create,modify,move "$UPLOAD_DIR" > /dev/null 2>&1
log_message "New certificates detected. Validating..."
# Validate the certificates before moving them
if validate_certs "$UPLOAD_DIR/public.pem" "$UPLOAD_DIR/private.pem"; then
# Move the new certificates to the cert directory
mv "$UPLOAD_DIR/public.pem" "$CERT_PATH"
mv "$UPLOAD_DIR/private.pem" "$KEY_PATH"
# Set secure permissions (allow Nginx to read, but nothing else)
chown www-data:www-data $CERT_PATH $KEY_PATH
chmod 600 $KEY_PATH
chmod 644 $CERT_PATH
# Reload Nginx to apply the new certificates
log_message "Valid certificates detected. Reloading Nginx..."
service nginx reload 2>&1 | tee -a $LOG_FILE
log_message "If it failed, then your keys didn't work. Maybe you mixed up public with private during the upload?"
# Restart Nginx for a clean start
service nginx restart 2>&1 | tee -a $LOG_FILE
log_message "Nginx restarted."
else
log_message "Invalid certificate or key. Skipping reload."
fi
# Sleep to avoid retriggering the inotifywait loop too quickly
sleep 30
done
Code: Select all
#!/bin/bash
WP_DIR="/var/www/html"
# Path to PHP executable
PHP_BIN="/bin/php8.1" # Adjust if PHP is installed elsewhere
# Check if WordPress is installed by looking for wp-config.php
if [ -f "$WP_DIR/wp-config.php" ]; then
echo "WordPress is installed. Triggering wp-cron.php directly..."
# Execute wp-cron.php using the PHP CLI
$PHP_BIN $WP_DIR/wp-cron.php
else
echo "WordPress is not installed yet. Skipping wp-cron.php trigger."
fi
Code: Select all
FROM evernode/sashimono:hp.latest-ubt.20.04
# Set noninteractive to avoid prompts during installation
ENV DEBIAN_FRONTEND=noninteractive
# Update and install Nginx, PHP, and MySQL
RUN apt-get update
RUN apt-get install -y nginx inotify-tools cron
RUN apt-get install -y wget curl mariadb-server mariadb-client openssl net-tools
RUN apt-get install -y software-properties-common && \
add-apt-repository ppa:ondrej/php && \
apt-get update
RUN apt-get install -y \
php8.1 \
php8.1-fpm \
php8.1-mysql \
php8.1-gd \
php8.1-xml \
php8.1-mbstring \
php8.1-curl \
php8.1-zip \
php8.1-intl \
php8.1-imagick
RUN mkdir -p /var/www/html && \
wget https://wordpress.org/latest.tar.gz && \
tar -xvzf latest.tar.gz && \
rm latest.tar.gz && \
mv wordpress/* /var/www/html/
RUN mkdir -p /var/www/ssl
RUN chown -R www-data:www-data /var/www/ssl && \
find /var/www/ssl -type d -exec chmod 755 {} \; && \
find /var/www/ssl -type f -exec chmod 644 {} \;
RUN chown -R www-data:www-data /var/www/html && \
find /var/www/html -type d -exec chmod 755 {} \; && \
find /var/www/html -type f -exec chmod 644 {} \;
RUN mkdir -p /var/www/html/wp-content/plugins/ssl-cert-uploader/
RUN chown -R www-data:www-data /var/www/html/wp-content/plugins/ssl-cert-uploader/;
RUN mkdir -p /var/www/html/wp-content/plugins/evercurl/
RUN chown -R www-data:www-data /var/www/html/wp-content/plugins/evercurl/;
RUN mkdir -p /var/www/html/wp-content/plugins/hotpocket-viewer/
RUN chown -R www-data:www-data /var/www/html/wp-content/plugins/hotpocket-viewer/;
RUN mkdir -p /var/www/ssl/uploads
RUN chown -R www-data:www-data /var/www/ssl/uploads;
# Generate a self-signed SSL certificate for HTTPS
RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /var/www/ssl/private.pem \
-out /var/www/ssl/public.pem \
-subj "/CN=localhost"
# Expose all necessary ports (no dynamic ports, just expose the range)
EXPOSE 36525-36535
# Copy start script and Nginx configuration template
COPY start.sh /start.sh
COPY certwatcher.sh /certwatcher.sh
COPY nginx-wordpress.conf /etc/nginx/sites-available/wordpress
COPY ssl-cert-uploader.php /var/www/html/wp-content/plugins/ssl-cert-uploader/ssl-cert-uploader.php
COPY evercurl.php /var/www/html/wp-content/plugins/evercurl/evercurl.php
COPY hotpocket-viewer.php /var/www/html/wp-content/plugins/hotpocket-viewer/hotpocket-viewer.php
# Set execute permission on the start script
RUN chmod +x /start.sh
RUN chmod +x /certwatcher.sh
#NGINX configs
RUN echo "memory_limit = 512M" >> /etc/php/8.1/fpm/php.ini && \
echo "upload_max_filesize = 64M" >> /etc/php/8.1/fpm/php.ini && \
echo "post_max_size = 64M" >> /etc/php/8.1/fpm/php.ini && \
echo "max_input_vars = 5000" >> /etc/php/8.1/fpm/php.ini
# Default working directory for WordPress
WORKDIR /var/www/html
#CRON For WP
RUN echo "*/1 * * * * /check-wp-cron.sh > /dev/null 2>&1" > /etc/cron.d/wp-cron
COPY check-wp-cron.sh /check-wp-cron.sh
RUN chmod +x /check-wp-cron.sh
# Apply permissions for cron job file
RUN chmod 0644 /etc/cron.d/wp-cron
# Apply the cron job to the cron service
RUN crontab /etc/cron.d/wp-cron
# Set entry point to start Nginx, MySQL, and PHP-FPM
ENTRYPOINT ["/start.sh"]
Code: Select all
<?php
/*
Plugin Name: EverCurl
Description: Sends a custom cURL request every X minutes with dynamic domain and port placeholders.
Version: 1.0
Author: Evernode Wordpress
*/
function wp_curl_sender_log_file_path() {
return plugin_dir_path(__FILE__) . 'curl_log.txt';
}
// Function to send the custom cURL request with dynamic placeholder replacements
function send_custom_curl_request() {
$curl_command = get_option('wp_curl_sender_command', '');
// Fetch WordPress site URL and extract domain and port
$site_url = get_option('siteurl');
$parsed_url = parse_url($site_url);
$domain = $parsed_url['host'];
$port = isset($parsed_url['port']) ? $parsed_url['port'] : '80'; // Default to port 80 if not found
// Replace placeholders {domain} and {port} in the cURL command
$curl_command = str_replace('{domain}', $domain, $curl_command);
$curl_command = str_replace('{port}', $port, $curl_command);
if ($curl_command) {
// Log the final cURL command with placeholders replaced
wp_curl_sender_log_message("Executing cURL command: $curl_command");
// Execute the cURL command
exec($curl_command . ' 2>&1', $output, $return_var);
// Log the output or handle errors
if ($return_var !== 0) {
$message = 'Curl error: ' . implode("\n", $output);
} else {
$message = 'Curl success: ' . implode("\n", $output);
}
// Log the message
wp_curl_sender_log_message($message);
} else {
wp_curl_sender_log_message("No cURL command found.");
}
}
// Function to log messages
function wp_curl_sender_log_message($message) {
$log_file = wp_curl_sender_log_file_path();
$time = date('Y-m-d H:i:s');
// Write to log file
if (is_writable(dirname($log_file))) {
file_put_contents($log_file, "[$time] $message\n", FILE_APPEND);
}
}
// Function to display log in the admin area with scrollbar
function wp_curl_sender_display_log() {
$log_file = wp_curl_sender_log_file_path();
if (file_exists($log_file)) {
$log_content = file_get_contents($log_file);
echo '<h2>cURL Log</h2>';
echo '<pre style="background-color: #f5f5f5; padding: 10px; border: 1px solid #ddd; max-height: 300px; overflow: auto;">' . esc_html($log_content) . '</pre>';
} else {
echo '<p>No logs available yet.</p>';
}
}
// Schedule the event dynamically based on saved interval
function wp_curl_sender_schedule_event() {
$interval = get_option('wp_curl_sender_interval', 5); // Default to 5 minutes
if (!wp_next_scheduled('send_custom_curl_request_hook')) {
wp_schedule_event(time(), 'custom_interval', 'send_custom_curl_request_hook');
}
}
add_action('init', 'wp_curl_sender_schedule_event');
// Custom interval for scheduling
function wp_curl_sender_custom_intervals($schedules) {
$interval = get_option('wp_curl_sender_interval', 5); // Default to 5 minutes
$schedules['custom_interval'] = array(
'interval' => $interval * 60, // Convert minutes to seconds
'display' => __('Custom Interval')
);
return $schedules;
}
add_filter('cron_schedules', 'wp_curl_sender_custom_intervals');
// Add action for the scheduled hook
add_action('send_custom_curl_request_hook', 'send_custom_curl_request');
// Plugin activation: Schedule the event
function wp_curl_sender_activation() {
wp_curl_sender_schedule_event();
}
register_activation_hook(__FILE__, 'wp_curl_sender_activation');
// Plugin deactivation: Clear scheduled events
function wp_curl_sender_deactivation() {
wp_clear_scheduled_hook('send_custom_curl_request_hook');
}
register_deactivation_hook(__FILE__, 'wp_curl_sender_deactivation');
// Add a top-level menu in the WordPress admin dashboard
function wp_curl_sender_add_admin_menu() {
add_menu_page(
'EverCurl', // Page title
'EverCurl', // Menu title
'manage_options', // Capability
'wp-evercurl', // Menu slug
'wp_curl_sender_options_page', // Callback function to display the page
'dashicons-admin-generic', // Icon
6 // Position in the menu
);
}
add_action('admin_menu', 'wp_curl_sender_add_admin_menu');
// Settings page content
function wp_curl_sender_options_page() {
// Fetch WordPress site URL and extract domain and port
$site_url = get_option('siteurl');
$parsed_url = parse_url($site_url);
$domain = $parsed_url['host'];
$port = isset($parsed_url['port']) ? $parsed_url['port'] : '80'; // Default to port 80 if not found
?>
<div class="wrap">
<h1>EverCurl Settings</h1>
<form method="post" action="options.php">
<?php
settings_fields('wp_curl_sender_settings_group');
do_settings_sections('wp-evercurl');
submit_button();
?>
</form>
<h2>Website Information</h2>
<p><strong>Domain:</strong> <?php echo esc_html($domain); ?></p>
<p><strong>Port:</strong> <?php echo esc_html($port); ?></p>
<?php
// Display the log
wp_curl_sender_display_log();
?>
</div>
<?php
}
// Register settings
function wp_curl_sender_settings_init() {
register_setting('wp_curl_sender_settings_group', 'wp_curl_sender_interval');
register_setting('wp_curl_sender_settings_group', 'wp_curl_sender_command'); // Store custom cURL command
add_settings_section(
'wp_curl_sender_section',
__('Set cURL Interval and Command', 'wp_curl_sender'),
null,
'wp-evercurl'
);
add_settings_field(
'wp_curl_sender_interval_field',
__('Interval in minutes', 'wp_curl_sender'),
'wp_curl_sender_interval_field_render',
'wp-evercurl',
'wp_curl_sender_section'
);
add_settings_field(
'wp_curl_sender_command_field',
__('Custom cURL Command (Use {domain} and {port} placeholders)', 'wp_curl_sender'),
'wp_curl_sender_command_field_render',
'wp-evercurl',
'wp_curl_sender_section'
);
}
add_action('admin_init', 'wp_curl_sender_settings_init');
// Render the interval input field
function wp_curl_sender_interval_field_render() {
$interval = get_option('wp_curl_sender_interval', 5); // Default to 5 minutes
?>
<input type="number" name="wp_curl_sender_interval" value="<?php echo esc_attr($interval); ?>" min="1" />
<p class="description">Enter the interval in minutes for the cURL request.</p>
<?php
}
// Render the custom cURL command input field
function wp_curl_sender_command_field_render() {
$command = get_option('wp_curl_sender_command', ''); // Default empty
?>
<input type="text" name="wp_curl_sender_command" value="<?php echo esc_attr($command); ?>" style="width:100%;" />
<p class="description">Enter the full cURL command. Use {domain} and {port} placeholders, if you need them, to add the domain and port from your wordpress settings (e.g., curl https://api.service/api?domain={domain}&port={port}).</p>
<?php
}
Code: Select all
<?php
/*
Plugin Name: Hotpocket Log Viewer
Description: A plugin to view and scroll the hotpocket log file.
Version: 1.0
Author: Evernode Wordpress
*/
add_action('admin_menu', 'hotpocket_log_viewer_menu');
function hotpocket_log_viewer_menu() {
add_menu_page('Hotpocket Log Viewer', 'Hotpocket Log Viewer', 'manage_options', 'hotpocket-log-viewer', 'hotpocket_log_viewer_page');
}
function hotpocket_log_viewer_page() {
$log_file = '/var/www/html/hotpocket.log'; // Path to your log file
// Check if the log file exists
if (!file_exists($log_file)) {
echo "<h3>Log file not found!</h3>";
return;
}
// Display log file contents
echo '<div style="max-height: 500px; overflow-y: scroll; border: 1px solid #ccc; padding: 10px;">';
// Read the log file and display it with HTML line breaks
$log_content = file_get_contents($log_file);
echo nl2br(htmlspecialchars($log_content));
echo '</div>';
}
// Ensure log file size does not exceed 1MB
add_action('init', 'hotpocket_log_viewer_limit_log_size');
function hotpocket_log_viewer_limit_log_size() {
$log_file = '/var/www/html/hotpocket.log'; // Path to your log file
$max_size = 104857; // 0.1MB in bytes
if (file_exists($log_file)) {
$log_size = filesize($log_file);
// Truncate the log file if it exceeds the maximum size
if ($log_size > $max_size) {
$content = file_get_contents($log_file);
// Keep the last 0.1MB of content
$trimmed_content = substr($content, -$max_size);
file_put_contents($log_file, $trimmed_content);
}
}
}
Code: Select all
server {
listen 36525 ssl;
listen 36526 ssl;
listen 36527 ssl;
listen 36528 ssl;
listen 36529 ssl;
listen 36530 ssl;
listen 36531 ssl;
listen 36532 ssl;
listen 36533 ssl;
listen 36534 ssl;
listen 36535 ssl;
listen 30132 ssl;
listen 30133 ssl;
listen 30135 ssl;
server_name localhost;
root /var/www/html;
index index.php;
# SSL certificate and key
ssl_certificate /var/www/ssl/public.pem;
ssl_certificate_key /var/www/ssl/private.pem;
# Recommended SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;
# Enable keepalive connections
keepalive_timeout 5s;
keepalive_requests 100;
client_max_body_size 64M;
location / {
try_files $uri $uri/ /index.php$is_args$args;
client_max_body_size 64M;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
ssl-cert-uploader.php
Code: Select all
<?php
/*
Plugin Name: SSL Certificate Uploader
Description: A plugin to upload SSL certificates for your domain.
Version: 1.0
Author: Evernode Wordpress
*/
add_action('admin_menu', 'ssl_cert_uploader_menu');
function ssl_cert_uploader_menu() {
add_menu_page('SSL Uploader', 'SSL Uploader', 'manage_options', 'ssl-cert-uploader', 'ssl_cert_uploader_page');
}
function ssl_cert_uploader_page() {
if (isset($_FILES['public_cert']) && isset($_FILES['private_key'])) {
// Validate file extensions for public certificate and private key
$public_cert_ext = pathinfo($_FILES['public_cert']['name'], PATHINFO_EXTENSION);
$private_key_ext = pathinfo($_FILES['private_key']['name'], PATHINFO_EXTENSION);
if ($public_cert_ext !== 'pem' || $private_key_ext !== 'pem') {
echo '<div class="notice notice-error"><p>Only .pem files are allowed!</p></div>';
} else {
// Path to upload directory
$upload_dir = '/var/www/ssl/uploads/';
// Move uploaded files to the upload directory
$public_cert_path = $upload_dir . 'public.pem';
$private_key_path = $upload_dir . 'private.pem';
move_uploaded_file($_FILES['public_cert']['tmp_name'], $public_cert_path);
move_uploaded_file($_FILES['private_key']['tmp_name'], $private_key_path);
echo '<div class="notice notice-success"><p>Certificates uploaded successfully!</p></div>';
}
}
// Display the upload form
?>
<div class="wrap">
<h1>Upload SSL Certificates</h1>
<form method="post" enctype="multipart/form-data">
<table class="form-table">
<tr>
<th><label for="public_cert">Public Certificate (public.pem)</label></th>
<td><input type="file" name="public_cert" required></td>
</tr>
<tr>
<th><label for="private_key">Private Key (private.pem)</label></th>
<td><input type="file" name="private_key" required></td>
</tr>
</table>
<p><input type="submit" class="button button-primary" value="Upload Certificates"></p>
</form>
</div>
<?php
}
start.sh
Code: Select all
#!/bin/bash
# Link Nginx configuration
ln -sf /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/
# Start MySQL service
service mysql start
# Create log files
touch /var/www/server.log
touch /var/www/html/hotpocket.log
chown www-data:www-data /var/www/html/hotpocket.log
chmod 664 /var/www/html/hotpocket.log
# Create WordPress database and user
mysql -e "CREATE DATABASE IF NOT EXISTS wordpress;"
mysql -e "CREATE USER IF NOT EXISTS 'wordpress'@'localhost' IDENTIFIED BY 'wordpress_password';"
mysql -e "GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost';"
mysql -e "FLUSH PRIVILEGES;"
# Start PHP-FPM service
service php8.1-fpm start
# Start Nginx service
service nginx start
service nginx reload
service cron start
nohup /certwatcher.sh &
#disable wpcron
#echo "define('DISABLE_WP_CRON', true);" >> /var/www/html/wp-config.php
# hotpocket log STDIN
hotpocket_log="/var/www/html/hotpocket.log"
echo "Under developments, will arrive in the future." > /var/www/html/hotpocket.log
# Keep the container running
tail -f /dev/null