Category: Uncategorized

  • Simple contact form with html and php


    This code is a PHP-based contact form processor that sends emails using the msmtp command. It validates and sanitizes user input, composes an email with the user’s input, and sends it to a hardcoded recipient email address. The code prioritizes security and includes error handling and debugging mechanisms. To use this code, you’ll need to have your own email account that can work with an msmtp configuration, as it relies on this setup to send emails. Overall, the code provides a solid foundation for a contact form processor, and with some customizations, it can become even more robust and feature-rich. Expansion opportunities include adding support for multiple recipient email addresses, implementing CAPTCHA or anti-spam measures, integrating with popular email services, and allowing file attachments.

    HTML

    <style>
        /* Set a max width for the form */<br />
        form {<br />
            width: 100%;<br />
            max-width: 600px;  /* Adjust the max-width as needed */<br />
            margin: 0 auto;  /* Center the form */<br />
            padding: 20px;<br />
            background-color: #f9f9f9;<br />
            border-radius: 8px;<br />
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);<br />
        }</p>
    <p>    /* Style the labels */<br />
        label {<br />
            display: block;<br />
            margin-bottom: 8px;<br />
            font-weight: bold;<br />
        }</p>
    <p>    /* Style the input fields */<br />
        input[type="text"],<br />
        input[type="email"] {<br />
            width: 100%;  /* Make input fields full width */<br />
            padding: 10px;<br />
            margin-bottom: 15px;<br />
            border: 1px solid #ccc;<br />
            border-radius: 4px;<br />
            box-sizing: border-box;  /* Include padding in the width calculation */<br />
        }</p>
    <p>    /* Style the textarea */<br />
        textarea {<br />
            width: 100%;  /* Make the textarea full width */<br />
            height: 150px;  /* Increase the height of the message box */<br />
            padding: 10px;<br />
            margin-bottom: 15px;<br />
            border: 1px solid #ccc;<br />
            border-radius: 4px;<br />
            box-sizing: border-box;  /* Include padding in the width calculation */<br />
        }</p>
    <p>    /* Style the submit button */<br />
        button {<br />
            padding: 12px 20px;<br />
            background-color: #4CAF50;  /* Green background */<br />
            color: white;<br />
            border: none;<br />
            border-radius: 4px;<br />
            cursor: pointer;<br />
            font-size: 16px;<br />
        }</p>
    <p>    button:hover {<br />
            background-color: #45a049;  /* Slightly darker green on hover */<br />
        }</p>
    <p>    /* Add small text for the message character limit */<br />
        .char-limit {<br />
            font-size: 14px;<br />
            color: #888;<br />
            margin-bottom: 10px;<br />
        }<br />
    </style>
    <form action="/contact-process-form.php" method="POST">
        <input type="hidden" name="nonce" value="<?php echo $nonce; ?>"></p>
    <p>    <label for="name">Name:</label><br />
        <input type="text" id="name" name="name" required maxlength="100"></p>
    <p>    <label for="email">Email:</label><br />
        <input type="email" id="email" name="email" required maxlength="100"></p>
    <p>    <label for="subject">Subject:</label><br />
        <input type="text" id="subject" name="subject" required maxlength="100"></p>
    <p>    <label for="message">Message:</label><br />
        <textarea id="message" name="message" required maxlength="6000"></textarea></p>
    <p>    <!-- Add a character limit notice --><br />
        <small class="char-limit">Maximum characters allowed in message: 6000</small><br /> <!-- Added line break --></p>
    <p>    <button type="submit">Send</button><br />
    </form>

    php contact-process-form.php in root folder of website.

    <?php
    // Load WordPress functions (adjust path if necessary)
    require_once('/var/www/bashing.life/wp-load.php');
    
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
    
    // Log errors to a secure file instead of displaying
    ini_set('log_errors', 1);
    ini_set('error_log', '/var/log/php_errors.log');
    
    // Define the path to msmtp (adjust according to where msmtp is installed)
    $msmtp_path = '/usr/bin/msmtp';  // Replace with your msmtp path if it's different
    
    // Define the path to the msmtp configuration file
    $msmtp_config_file = '/etc/msmtprc';  // Replace with the correct path to the msmtp configuration file
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Sanitize and retrieve the form data
        $name = sanitize_text_field($_POST['name']);
        $email = sanitize_email($_POST['email']);
        $subject = sanitize_text_field($_POST['subject']);
        $message = wp_kses_post($_POST['message']);  // Sanitizing message further
    
        // Validate the email address
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            echo "Invalid email format.";
            exit;
        }
    
    // Escape each variable to prevent command injection
        $safe_name = escapeshellarg($name);
        $safe_email = escapeshellarg($email);
        $safe_subject = escapeshellarg($subject);
        $safe_message = escapeshellarg($message);
    
        $hardcoded_subject = "New Contact Form Submission";
    
        $email_content = "You have received a new contact form submission:\n\n";
        $email_content .= "Name: $safe_name\n";
        $email_content .= "Email: $safe_email\n";
        $email_content .= "Subject (from form): $safe_subject\n\n";
        $email_content .= "Message:\n$safe_message\n";
    
     // Escape the full email content (to ensure no issues with newlines, quotes, etc.)
        $escaped_email_content = escapeshellarg($email_content);
    
        // Send the email using msmtp
        $recipient = 'example@example.com';  // Your recipient email address
    
        // Properly escape the command to avoid injection
    
        $command = "echo \"Subject:$hardcoded_subject\nContent-Type: text/plain; charset=UTF-8\n\n$email_content\" | $msmtp_path --file=$msmtp_config_file -a default example@example.com";
    
    
        // Execute the command and capture the output
        $output = shell_exec($command . ' 2>&1');  // Capture both stdout and stderr
    
        // Debugging: Check if output is captured and display it
        if ($output === null) {
            echo "Failed to send the message. No output received from msmtp.";
        } else {
            //echo "Message sent successfully!<br>"; //for future debugging
            //echo "<strong>Debugging Output from msmtp:</strong><br>"; //for future debugging
            //echo "<pre>$output</pre>";  // Debugging output from msmtp
            // Redirect to the thank-you page after success
            header("Location: thank-you");
            exit;
        }
    
    } else {
        echo "Please fill in all fields.";
    }
    ?>
    
    

    The msmtprc system-wide configuration file must be in /etc

  • Fixing Proxmox SSL Certificates Behind HAProxy: Step-by-Step Tutorial

    This tutorial chronicles my troubleshooting and resolution process for SSL certificate issues with Proxmox when running behind an OpenWRT router with HAProxy. It includes all intermediate steps, challenges, and reasoning, as well as interactions with my assistant, which helped guide the solution.


    1. Network Context and Initial Problem

    My setup was as follows:

    ISP Router -> OpenWRT (DMZ) with HAProxy -> Proxmox
    • Proxmox listens on its default HTTPS port 8006.
    • Other internal web servers are served through HAProxy, which handles TLS for domains like aurispetreus.net.
    • My goal was to have valid, trusted TLS certificates for Proxmox while maintaining other web services on the same HAProxy instance.

    Problem Observed

    When attempting to access Proxmox through its domain (for example, using the Proxmox Android app), I encountered SSL protocol errors. Initially, I wasn’t sure whether:

    1. Proxmox was failing to pick up the manually uploaded certificates.
    2. HAProxy configuration was interfering with TLS termination.
    3. File permissions or certificate formats were incorrect.

    2. Generating and Copying Certificates

    Proxmox could not directly request Let’s Encrypt certificates because it wasn’t publicly accessible from the internet. Therefore, I generated certificates on a separate Debian server (debian) that could reach Let’s Encrypt:

    certbot certonly -d da3.aurispetreus.net

    This created:

    /etc/letsencrypt/archive/da3.aurispetreus.net/fullchain1.pem
    /etc/letsencrypt/archive/da3.aurispetreus.net/privkey1.pem

    I then manually copied these files to Proxmox (proxmox A) and renamed them to match Proxmox’s expected filenames:

    /etc/pve/local/pve-ssl.pem       # fullchain
    /etc/pve/local/pve-ssl.key       # private key

    I also set proper permissions for the private key:

    chmod 600 /etc/pve/local/pve-ssl.key

    At this point, I suspected that the certificates might not be correctly paired, so I decided to verify them.


    3. Verifying Certificate and Key Match

    To ensure the certificate and private key matched, I used OpenSSL to check the modulus:

    openssl x509 -noout -modulus -in fullchain1.pem | openssl md5
    openssl rsa -noout -modulus -in privkey1.pem | openssl md5

    Initially, I received:

    Not an RSA key

    This indicated a misuse of commands or possibly a non-RSA key (like EC). After adjusting the method (taking into account the correct type of key generated by Certbot), I confirmed that the modulus of the certificate and the private key matched, verifying they belonged together.


    4. HAProxy Configuration for Proxmox

    Since Proxmox sits behind HAProxy, it was important that TLS traffic be forwarded correctly. I started with the following principles:

    1. Use TCP mode on HAProxy for port 443 to allow end-to-end TLS (Proxmox manages its own certificates).
    2. Route traffic using SNI to direct specific domains to their respective backend servers.
    3. Keep the standard HTTPS port (443) public, without binding Proxmox’s internal port 8006 on HAProxy.

    Here’s the relevant HAProxy frontend for HTTPS:

    frontend https_in
        bind *:443
        mode tcp
        option tcplog
    
        # Inspect TLS handshake for SNI
        tcp-request inspect-delay 5s
        tcp-request content accept if { req_ssl_hello_type 1 }
    
        # Use backend based on requested SNI
        use_backend %[req.ssl_sni,lower]_tls if { req.ssl_sni -m found }

    And the Proxmox backend:

    backend da3.aurispetreus.net_tls
        mode tcp
        option tcp-check
        server da3.aurispetreus.net 192.168.3.144:8006 check
    # Note: No need to bind HAProxy to port 8006 externally

    Important detail: The assistant reminded me explicitly:
    “No, you do not bind port 8006 on HAProxy, because HAProxy is meant to expose a public listener on the standard HTTPS port (443).”
    This avoids exposing internal Proxmox ports directly and keeps the network cleaner.


    5. Initial Testing

    I tested the TLS handshake locally:

    curl -vk https://192.168.3.144:8006/

    Output confirmed that TLSv1.3 was being used, and the correct certificate was presented.

    Externally, trying to access 188.21.68.172:8006 failed because the ISP router blocked the direct connection, which was expected.


    6. Debugging “Wrong Version Number” Error

    When testing public access via HAProxy:

    curl -vk https://da3.aurispetreus.net:443

    I got:

    error:0A00010B:SSL routines::wrong version number

    Analysis:

    • The HAProxy frontend for port 443 was in HTTP mode.
    • TLS traffic to Proxmox requires TCP passthrough, not HTTP mode.

    Solution: Change the frontend to TCP mode. After doing so, the handshake worked correctly, and the Proxmox Android app could connect.

  • Cockpit via Apache Reverse Proxy

    I know, this is very unsafe if your login credentials are stolen but it serves as a proof of concept!!!

    1. The Goal

    To provide secure, external access to the Cockpit Web Console via https://example.site/cockpit/ without opening extra ports (like 9090) on the router and without managing separate SSL certificates for the dashboard.

    2. Architecture Flow

    1. User requests https://example.site/cockpit/.
    2. OpenWrt (HAProxy) receives traffic on port 443 and passes it to the Debian LXC (192.168.1.16:443).
    3. Apache (inside LXC) terminates the SSL using Let’s Encrypt certificates.
    4. Apache proxies the request internally to 127.0.0.1:9090.
    5. Cockpit processes the request and responds through the tunnel.

    3. Key Configurations

    A. Apache VirtualHost (/etc/apache2/sites-available/...)

    We used mod_proxy and mod_rewrite to handle both standard web traffic and the persistent WebSocket connections required for the Cockpit terminal.

    # WebSocket Upgrade (Fixes "Login then Blank Page" issue)
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /cockpit/(.*) ws://127.0.0.1:9090/cockpit/$1 [P,L]
    
    # Subfolder Proxy
    <Location /cockpit/>
        ProxyPass http://127.0.0.1:9090/cockpit/
        ProxyPassReverse http://127.0.0.1:9090/cockpit/
    </Location>

    B. Cockpit Config (/etc/cockpit/cockpit.conf)

    Crucial for preventing CSRF (Cross-Site Request Forgery) security blocks and allowing the unencrypted internal “handshake.”

    [WebService]
    Origins = https://hissite.org
    ProtocolHeader = X-Forwarded-Proto
    AllowUnencrypted = true
    UrlRoot = /cockpit

    4. Troubleshoot Log & Fixes

    • Issue:sscg: command not found.
      • Fix: Installed sscg package to satisfy Cockpit dependencies.
    • Issue:gnutls_handshake failed: A TLS fatal alert.
      • Reason: Cockpit was expecting HTTPS but Apache was sending HTTP.
      • Fix: Added AllowUnencrypted = true and restarted cockpit.socket.
    • Issue: Blank page after login.
      • Reason: WebSockets (the “live” part of the site) were failing to tunnel through Apache.
      • Fix: Added specific RewriteRule for ws:// protocol.

    5. Security Hardening Applied

    • Consolidated SSL: All traffic uses the main site’s hardened TLS 1.2/1.3 settings.
    • Internal Communication: Apache talks to Cockpit over 127.0.0.1 (local loopback), meaning no unencrypted traffic ever touches your LAN or the WAN.

    6. Maintenance Commands

    If Cockpit ever stops responding, run these in order:

    1. sudo systemctl restart cockpit.socket (Restarts the listener)
    2. sudo systemctl restart apache2 (Restarts the proxy)
    3. sudo journalctl -u cockpit -f (To watch live logs if a crash occurs)
  • GnuDIP

    GnuDIP2 can be used to update your ip address. The following configuration is going to be tailored to manage the free ddns service provided in ddns.freedombox.org or compatible ddns services.

    Before starting you need to already have a gnudip account and install the curl package (sudo apt install curl).

    Download the latest version of GnuDIP from the official site:
    https://gnudip2.sourceforge.net/gnudip-www/latest/gnudip/html/clients.html

    Direct link:
    https://gnudip2.sourceforge.net/gnudip-www/latest/gnudip/html/client/UNIX/gnudip-latest-gdipc.tar.gz

    Change to your root account: This is so that the gdipc script (necessary to update the ip address of your service) can be run at a regular basis without the need to log in with your personal user account. Just let the root user handle it and forget it…

    Decompress and untar the file to /usr/local/

    Now you need to add a new directory path to your $PATH so you can run the script at the location where the gdipc folder was placed. Recomended location /usr/local/

    Edit the .bashrc file of your root account and add the following line at the end:

    #DDNS GnuDIP update service
    export PATH="$PATH:/usr/local/gdipc/bin"

    Source the file to update your $PATH with:

    source ~/.bashrc

    Running gdipc.pl script to add a new entry. This entry will contain the necessary information so that GnuDIP2 logs in to your account and verify if it needs to update your ip address.

    When you run gdipc.pl with the -c option, it allows you to configure the script through an interactive setup. This can help avoid manual editing of the script and ensure all necessary settings are entered correctly. Additionally, the information you enter will be saved in .GnuDIP2 in the same root home directory.

    1. Run the Script with -c Option:To begin the configuration process, run the script with the -c flag: gdipc.pl -c
    2. Follow the Prompts:The script will prompt you for the required configuration details. You will need to input the following:
      • Username: Your user account created in ddns.freedombox.org
      • Domain: Depending on the subdomain chosen at the time of registration like fbx.one or freedombox.rocks.
      • Connect by direct TCP (d) or web server (w) [d]:
      • GnuDIP Server – host[:port]: ddns.freedombox.org
      • Password: Your password provided at registration.
      • Cache File [/root/.GnuDIP2.cache..]: Location of the cache file.
      • Minimum Seconds Between Updates [0]: Leave as default
      • Maximum Seconds Between Updates [2073600]: Leave as default
    3. Complete the Configuration:After entering all the necessary information, the script will configure itself with the values you’ve provided.
    4. Verify the Configuration:Once you’ve configured the script, you can run it without the -c option to check if the DDNS update process works correctly:
      gdipc.pl The script should now update the DDNS service with your current IP address and domain.
    5. Automate the Process :To ensure your IP is updated regularly, you can automate the execution of gdipc.pl using a cron job.
      • Here’s an example cron job that runs the script every 10 minutes:
      • crontab -e Add the following line to run the script every 10 minutes:
        • */10 * * * * /usr/local/gdipc/bin/gdipc.pl -q "curl -s ifconfig.me" >> /home/root/.GnuDIP2.log 2>&1
        • Note that “curl -s ifconfig.me” will retrieve you public ip address.
    6. Executing and Logging: As you can see in the cron job configured, the script needs to be executed with the -q option. This is because if its run without it, it will use the local ip address of your network and not your public ip address to perform the update. Additionally, is configured standard and error output to a log file to keep track of the script work.
    • With -q Option: You can specify a command to retrieve the IP address, and the script will execute that command.
    • Without -q Option: The script tries to obtain the local machine’s IP address via getsockname() on a socket connection.
    • With -g Option: If you’re behind a gateway, the script registers the external IP address that the GnuDIP server sees.

  • AdGuard Home (AGH) Port Conflict Resolution

    AdGuard Home (AGH) Port Conflict Resolution

    AdGuard Home requires ports 53 (DNS) and 80 (Web Interface), but OpenWrt’s core services (Dnsmasq and LuCI) already use them. Use the following steps via SSH to fix the conflicts.


    1. Resolve Port 53 Conflict (DNS)

    We move OpenWrt’s Dnsmasq server from port 53 to port 5353, allowing AGH to take the standard port 53.

    1. Stop AGH: /etc/init.d/adguardhome stop
    2. Move Dnsmasq to Port 5353 (Execute these three lines): uci set dnsmasq.@dnsmasq[0].port=’5353′ uci commit dnsmasq /etc/init.d/dnsmasq reload
    3. Configure AGH DNS Port: During the AGH welcome screen setup, set its DNS server port to 53.

    2. Resolve Port 80 Conflict (Web Interface)

    Since port 80 is used by LuCI and your reverse proxy, we change the AGH dashboard port.

    1. Configure AGH Web Port: During the AGH welcome screen setup, change the Web interface port from 80 to an available port like 8080 or 81.
      • Access: You will access the AGH dashboard via http://[OpenWrt_IP]:8080 (or the port you chose).

    3. Finalize Traffic Redirection

    Once AGH is set up and listening on port 53, you must redirect all DNS traffic hitting the router (which goes to Dnsmasq on port 5353) back to AGH on port 53.

    1. Add a Forward Rule and Restart Dnsmasq (Execute these three lines): uci set dnsmasq.@dnsmasq[0].server=’127.0.0.1#53′ uci commit dnsmasq /etc/init.d/dnsmasq restart

    4. Verification

    • Check the AGH dashboard at your chosen port (e.g., http://[OpenWrt_IP]:8080).
    • Ensure devices are successfully being filtered.
  • Guide: Installing Realtek RTL8125 Driver on Debian/Proxmox

    This guide resolves issues encountered when installing the TP-Link TX21/Realtek RTL8125 driver on a Debian-based system (like Proxmox) that already has an older Realtek NIC using the built-in r8169 driver.

    Prerequisites

    • Console Access: Since network connectivity will be interrupted, you must have physical access (keyboard/monitor) or IPMI/iDRAC/vPro access.
    • Latest Driver: Ensure you download the latest Linux driver tarball for the RTL8125 chip directly from the Realtek website to avoid compilation errors with newer kernels.
    • Kernel Headers & DKMS: Install the necessary packages for compiling the driver:Bash
    apt update
    apt install build-essential dkms pve-headers

    1. Resolve Driver Installation Failure (The Hang)

    The installer (autorun.sh) will hang because the kernel’s default r8169 driver is actively in use by another NIC.

    A. Identify and Disable the Conflicting Interface

    1. Find the Physical Interface and Bridge: Use ip a to identify the active interface using the r8169 driver (e.g., enp6s0) and the corresponding Proxmox bridge (e.g., vmbr1).
    2. Bring Down the Network: This will stop the driver’s process, allowing the installer to unload it.
    ifdown vmbr1               # Bring down the bridge (crucial for Proxmox)
    ip link set enp6s0 down    # Bring down the physical interface
    • Manually Unload the Old Driver:
    rmmod r8169

    B. Run the Installation

    1. Navigate to the new driver directory (e.g., cd r8125-9.011.00).
    2. Execute the installer:
    ./autorun.sh

    If successful, the script will compile the new r8125 module using DKMS and install it.

    Reboot:

    reboot

    2. Resolve NIC Conflict (Missing Interfaces)

    After installation, the new r8125 driver often aggressively claims both the new NIC and the older onboard NIC, leaving the older one unusable.

    The solution is to bind the custom r8125 driver only to the new card, leaving the old card for the kernel’s r8169 driver.

    A. Identify the New Card’s PCI Address

    1. List PCI devices and find the new Realtek card: Look for the device actively using the r8125 driver and note its PCI address (e.g., 04:00.0).
    lspci -nnk | grep -i realtek -A3

    B. Create a Driver Binding Rule

    1. Create the configuration file: This uses the install directive to tell the system to load r8125 but only bind it to the specified PCI address.
    # REPLACE [NEW_CARD_PCI_ADDRESS] with your actual address (e.g., 04:00.0)
    echo 'install r8125 /sbin/modprobe --ignore-install r8125; /usr/bin/echo "[NEW_CARD_PCI_ADDRESS]" > /sys/bus/pci/drivers/r8125/bind' > /etc/modprobe.d/r8125-bind.conf
    • Update Initramfs: This applies the new binding rule before the kernel loads drivers.
    update-initramfs -u -k all

    and reboot now.

    3. Final Verification

    After the final reboot, both network interfaces should be present and active:

    • Check all interfaces:Bash
    ip a
    • Verify drivers:
      • The new TX21 card should show driver: r8125.
      • The old onboard NIC (e.g., enp6s0) should show driver: r8169.
  • OpenWrt MT7610U AP Install and config AP

    1. ⚙️ Hardware & Driver Installation

    The core task was installing the correct drivers and firmware for your MediaTek MT7610U chipset (Vendor ID 0b05:17d1).

    • Driver: Installed the kernel module responsible for the MT76x0 series USB chips:
      • opkg install kmod-mt76x0u
    • Firmware: Installed the required binary data for the chip to function (named for a similar chip in the family):
      • opkg install mt7601u-firmware
    • AP Management: Installed the daemon necessary to run a Wi-Fi Access Point and handle modern encryption:
      • opkg install hostapd-wolfssl (Chosen for full WPA3 support with a lightweight security library.)

    2. 🔒 Wireless Security (Encryption)

    You selected the strongest security suitable for a simple, password-based AP.

    • Protocol Choice: WPA3-SAE (Simultaneous Authentication of Equals).
    • Reasoning:
      • Simplicity: Uses a passphrase, eliminating the need for a complex RADIUS server (which would be required by WPA3-EAP/Enterprise).
      • Security: Provides modern protection against offline dictionary attacks, which WPA2-PSK is vulnerable to.

    3. 📶 Wireless Mode & Compatibility

    You successfully configured the AP to run on the 2.4 GHz band while maintaining compatibility for legacy devices.

    • Band: 2.4 GHz (Chosen for better range and penetration for remote management).
    • Mode: N (802.11n).
    • Compatibility: Selecting N mode allows all modern 802.11n devices to connect, while simultaneously enabling 802.11g mode, ensuring your older 2.4 GHz-only client can connect successfully.
    • Channel Width: (Implicitly or explicitly set to) 20 MHz (HT20) for maximum stability and compatibility in the congested 2.4 GHz band.
  • Ansible and System Updates

    apt update
    apt install ansible
    apt install software-properties-common

    Setup passwordless authentication with the servers you want to manage

    This is useful if you want to automate processes via ssh without intervening by having to input the password to the remote server.

    sshcopy-id user@ip_address

    Setup passwordless sudo commands

    In the server you want to manage with ansible, you’ll have to allow the sudo user to execute commands without a password. Since in this example we want to automate the updates-upgrades of the system, each time ansible sends the order to do an apt update (or other command with superuser privileges) the sudo password will be asked demanding a human interaction and ansible will not be able to successfully send the order to the remote server. There are 2 ways to do this. One is by typing visudo in debian to ope the configuration file /etc/sudoers and the other ways is by creating a new file inside /etc/sudoers.d/[name of your sudo user]. Once inside that file we need to add the following line:

    # Allow admin user to run specific scripts and commands with superuser privileges
    # without needing a password.
    admin ALL=(ALL) NOPASSWD: ALL

    If you want to be less permissive and only allow certain commands to be executed without a password these are the ones used in the playbooks in this example:

    # Allow admin user to run specific scripts and commands with superuser privileges
    # without needing a password.
    Cmnd_Alias UPDATE_PKGS = /usr/bin/apt update, \
                              /usr/bin/apt upgrade, \
                              /usr/bin/apt dist-upgrade, \
                              /usr/bin/apt autoremove, \
                              /usr/bin/apt autoclean, \
    admin ALL=(ALL) NOPASSWD: UPDATE_PKGS                          

    Define where your ansible files are going to be placed

    Given that this example will be tested in proxmox, I will locate them in /etc/ansible if is not created by defect. Furthermore, having the ansible configuration directtory in the main sever will ensure comms with all vms running inside unless the server is shut down.

    Create an inventory file that includes your Proxmox VMs (or other servers you want to manage). For example, /etc/ansible/hosts:

    Make sure your vms have a static ip!

    [proxmox_vms]
    vm1 ansible_host=192.168.1.101 ansible_ssh_user=admin
    vm2 ansible_host=192.168.1.102 ansible_ssh_user=admin

    Test your connectivity with the hosts:

    ansible all -m ping

    1. Daily Tasks Playbook: daily_tasks.yml

    ---
    - name: Daily Maintenance Tasks
      hosts: webservers
      become: yes
      tasks:
        - name: Update the apt package cache
          apt:
            update_cache: yes
    
        - name: Clean up unused packages
          apt:
            autoremove: yes
    
        - name: Clear out outdated package files
          apt:
            autoclean: yes
    

    2. Monthly Tasks Playbook: monthly_tasks.yml

    ---
    - name: Monthly Maintenance Tasks
      hosts: webservers
      become: yes
      tasks:
        - name: Upgrade all installed packages (standard)
          apt:
            upgrade: safe  # Upgrades packages without removing any
    
        - name: Full upgrade of all installed packages (including system upgrades)
          apt:
            upgrade: dist  # Allows removal of obsolete packages and installation of new dependencies
    

    3. Emergency Security Updates Playbook: security_updates.yml

    ---
    - name: Urgent Security Upgrades
      hosts: webservers
      become: yes
      tasks:
        - name: Update the apt package cache
          apt:
            update_cache: yes
    
        - name: Upgrade only security updates
          apt:
            upgrade: dist  # Upgrades security updates only
    

    Create a simple bash script to manage the urgent security upgrades and make it executable:

    nano /etc/ansible/security_updates.sh

    #!/bin/bash
    # Check for security updates
    SECURITY_UPDATES=$(apt list --upgradable 2>/dev/null | grep -i security)
    if [ -n "$SECURITY_UPDATES" ]; then
    echo "Security updates available. Running the Ansible playbook."
    ansible-playbook -i /etc/ansible/hosts /etc/ansible/security_upgrades.yml
    else
    echo "No security updates available."
    fi

    chmod +x /etc/ansible/security_updates.sh

    Cron Jobs for Execution

    Now, here are the cron jobs to schedule the execution of these playbooks:

    1. Daily Maintenance Tasks: Run every day at 2 AM.
    0 2 * * * ansible-playbook -i /etc/ansible/hosts.ini /etc/ansible/daily_tasks.yml >> /var/log/ansible_daily.log 2>&1

    2. Monthly Maintenance Tasks: Run on the first Saturday of every month at 2 AM.

    0 2 * * 6 [ "$(date +\%d)" -le 7 ] && ansible-playbook -i /etc/ansible/hosts.ini /etc/ansible/monthly_tasks.yml >> /var/log/ansible_monthly.log 2>&1

    3. Emergency Security Updates: Run daily at 3 AM (or adjust as needed) using a script that checks for security updates.

    0 3 * * * /etc/ansible/security_updates.sh >> /var/log/ansible_security.log 2>&1  # Ensure the correct script name

    The end

  • Freedombox – Installation & Setup (Two ways)

    There are mainly two ways to get a Freedombox running. The first one would be to install Debian first and then the Freedombox package or the alternative to download the Freedombox image designed for your desired system.

    1- Install Debian

    Ideally you won’t need a desktop environment to run your debian with freedombox installed as all management tasks are done via its web interface.

    Give sudo privileges to your user account. Change to root user:

    su -

    usermod -aG sudo user

    Reboot your system for the changes to apply.

    Configure power management

    Disable auto sleep-hibernate otherwise otherwise your server might go to sleep in 20 mins regardless of what you have chosen in your power management settings. The reason is because when your server reboots next time and you log in only remotely, the power settings will default to system-wide options. We don’t want that to happen when managing a remote server specially if you want to have also a desktop environment. Let’s play safe and disable the relevant power management options:

    sudo systemctl mask sleep.target suspend.target suspend-then-hibernate.target hibernate.target hybrid-sleep.target

    Update your package list and system upgrades

    sudo apt-get update && sudo apt upgrade

    Install freedombox package.

    sudo DEBIAN_FRONTEND=noninteractive apt-get install freedombox

    or if you want to configure slapd and get your secret (a random string you have to use post installation in the freedombox web install)

    sudo apt install freedombox

    Open a web browser and go to the local ip address of your server. Finish the installation. Enter the secret password provided during the installation, log in and start installing apps. Recommended to start with wordpress to get your domain working with your new self hosted home page.


    Ensure the secondary ssd (if you have one installed) is configured to be auto mounted with the same drive id if your server reboots. Log in to plinth and go to cockpit –> drives. This is where the backups are going to be stored. Change the options to enable auto mounting and changing the name of the permanent mount to something easier like /media/root/backup.

    Log in to plinth

    Obtain an ssl certificates for your domain (if you bought one) or get a free domain at ddns.freedombox.org

    Log in to your DNS service provider and map your domain name to the public ip of your edge router. Use ‘curl ifconfig.me’ for Linux and Windows or ‘Invoke-RestMethod ifconfig.me’ for windows to know your public ip. Freedombox also provides a free domain of your choice like yourdomain.fbx.one or domain.freedombox.rocks.

    Go to system –> Configure. Remove you first domain and type the other one you have. Update the configuration. You should loose connection to the freedombox site momentarily . Log back in with your private ip address and go to Configure –> Let’s Encrypt and click to obtain the certificate for your second domain. If you have your dns records setup correctly, both domain names should reach your freedombox.

    Go to system –> Let’s Encrypt and click “obtain” to get your certificate. Now you should go to your_domain instead of the ip address. If you have more subdomains now its the time to get those certificates as well.

    If you want to get your free domain:
    Go to system –> Dynamic DNS Client and complete the information required but first visit ddns.freedombox.org and create an account. The information to access that account will be necessary to configure your ddns settings.

    Install Packages

    Other necessary packages might be needed depending on the services your server will provide.

    1. mariadb-server – Necessary to install other packages like wordpress. The installation will also configure phpmyadmin to manage the databases in the web browser.
    2. php8.2 – Check for more updated available versions.
    sudo apt install mariadb-server php8.2 php8.2-imagick php-imagick php8.2-intl

    Install Freedombox from a predefined image

    Go to https://freedombox.org/download/ and select the qemu image to install it in a Proxmox virtual machine. In this example we’ll choose the quemu/kvmamd64. Copy the download link address to use it later.

    Import qcow2 disk to VM

    Overview: Create a vm with parameters you want. The disk will be detached and deleted later so you have attach the qcow2 disk to it. Download the qcow2 image to your proxmox then move the virtual disk to the location where the virtual disks are stored for your VMs. You have to assign the id number of the vm of interest to the disk when doing the import. Proxmox won’t be able to download the image using its own agent to pull the image to the default destination folder because its compressed. Instead, open a shell in proxmox and go to the following location and download it using wget and decompress using [xz -d image_file.xz].

    Now you are able to import the image to your VM (make sure your vm does not have a any disks to avoid any confusion)

    qm importdisk [vm id number] freedombox-bookworm_all-amd64.qcow2 local-lvm

    Back in Proxmox web UI, select the VM you just created and attach the disk to the VM in the hardware section and continue in options and make it bootable. After that you can turn on the VM to access its web interface to complete the setup process. Once finished its ready to use and install apps.

    Done.

    Sources:
    https://wiki.debian.org/FreedomBox/Manual

  • Wake On Lan

    List the interfaces on your system for proper identification. The ip command will inform the names and states of them.

    ip a

    Execute ethtool with the name of the interface that must be configured to wake on lan.

    sudo ethtool [interface] 

    To enable WOL on an interface (non-persistent) type:

    sudo ethtool -s [interface] wol g

    To make a persistent change in the interface edit the /etc/network/interfaces.d/eth0 (or modify the global interface config file /etc/network/interfaces):

    auto eth0
    iface eth0 inet dhcp
         ethernet-wol g

    OR

    auto eth0
    iface eth0 inet dhcp
         post-up ethtool -s [interface] wol g

    Another way is to edit the main configuration file and add the following instruction at the end of the file:

    post-up /usr/sbin/ethtool -s [interface] wol g

    The post-up command will trigger the execution of the ethtool command on the selected interface after the interface has been initialized.

    etherwake, wakeonlan, gwakeonlan

    Wake-on: g means it is enabled.