Wordpress On Evernode With Working SSL + Dynamic DNS

Post your evernode tutorials and ideas here!

Moderator: EvrSteward

Post Reply
everwordpress
Posts: 1
Joined: Mon Sep 09, 2024 10:23 pm

Wordpress On Evernode With Working SSL + Dynamic DNS

Post by everwordpress »

You can easily boot up a wordpress installation on wordpress, in this post you'll see the necessary files to build a docker instance, and an initial idea of how to solve the SSL + DNS challenges.

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
check-wp-cron.sh

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
Dockerfile

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"]
evercurl.php

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
}
hotpocket-viewer.php (haven't had much success with this one yet)

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);
        }
    }
}
nginx-wordpress.conf

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
Post Reply