Author: Zhe Yuan

  • Generating Your Own Self-Signed CA and HTTPS Certificates with OpenSSL

    Last Updated: May 17, 2025

    In this post, I’ll guide you through the process of creating your own Certificate Authority (CA) and using it to sign server certificates for HTTPS. This is particularly useful for development environments, internal services, or any situation where you need SSL/TLS encryption without purchasing certificates from a commercial CA. We’ll be using OpenSSL, a powerful and versatile command-line tool.

    Prerequisites:

    • OpenSSL installed on your system.

    Let’s dive into the steps.

    Step 1: Generate the CA Private Key (SelfSignedCA.key)

    The first step is to create a private key for your self-signed CA. This key is the foundation of your CA’s trustworthiness; keep it secure and private. If this key is compromised, any certificates signed by it could be forged.

    openssl genpkey -algorithm RSA -out SelfSignedCA.key -aes256 -pkeyopt rsa_keygen_bits:4096

    Let’s break down this command:

    • openssl genpkey: Command to generate a private key.
    • -algorithm RSA: Specifies the RSA algorithm for the key.
    • -out SelfSignedCA.key: The output filename for your CA private key.
    • -aes256: Encrypts the outputted private key with AES 256-bit encryption. You will be prompted for a passphrase to protect this key.
    • -pkeyopt rsa_keygen_bits:4096: Sets the key length to 4096 bits, which is generally considered strong.

    Step 2: Prepare the CA Configuration File (openssl_ca.cnf)

    Before generating the CA root certificate, you need to create an OpenSSL configuration file. This file defines crucial properties and extensions for your CA certificate. Create a file named openssl_ca.cnf in your working directory with the following content:

    # openssl_ca.cnf
    [ req ]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_ca # The extensions to add to the self-signed cert
    
    [ req_distinguished_name ]
    # No specific entries needed here if providing subject via -subj command line argument
    # You could define default values or prompts here if not using -subj
    
    [ v3_ca ]
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always,issuer
    basicConstraints = critical, CA:TRUE
    keyUsage = critical, digitalSignature, cRLSign, keyCertSign

    Explanation of openssl_ca.cnf:

    • [ req ]: Basic request settings.
      • x509_extensions = v3_ca: Specifies the section (v3_ca) containing extensions for the self-signed CA certificate.
    • [ v3_ca ]: Defines the X.509 v3 extensions for the CA certificate.
      • subjectKeyIdentifier = hash: An identifier for the public key of this certificate.
      • authorityKeyIdentifier = keyid:always,issuer: An identifier for the public key of the certificate that signed this one. For a self-signed root, this refers to itself.
      • basicConstraints = critical, CA:TRUE: Crucial for a CA certificate. It marks the certificate as a CA, meaning it can be used to sign other certificates. critical means this extension must be understood by the application.
      • keyUsage = critical, digitalSignature, cRLSign, keyCertSign: Specifies the allowed uses for the CA’s key (signing other certificates, signing Certificate Revocation Lists).

    Step 3: Generate the CA Root Certificate (SelfSignedCA.pem)

    With the CA private key (SelfSignedCA.key) and the openssl_ca.cnf configuration file prepared, you can now generate the CA’s root certificate. This certificate is what you will install in browsers or systems to make them trust the certificates signed by your CA.

    openssl req -x509 -new -nodes -key SelfSignedCA.key -sha256 -days 7300 \
        -out SelfSignedCA.pem \
        -subj "/C=XX/ST=YourState/L=YourCity/O=Your Organization/OU=Your Unit/CN=Your Custom Root CA" \
        -config openssl_ca.cnf

    Command explanation:

    • openssl req -x509: Command for X.509 certificate signing request (CSR) management and certificate generation. The -x509 flag means we are creating a self-signed certificate.
    • -new: Indicates a new certificate request.
    • -nodes: If your CA private key (SelfSignedCA.key) is encrypted, this option allows you to proceed without encrypting the certificate itself (you’ll be prompted for the private key’s passphrase).
    • -key SelfSignedCA.key: Specifies the private key to use for signing the certificate.
    • -sha256: Uses the SHA-256 hash algorithm for the signature.
    • -days 7300: Sets the validity period of the certificate (7300 days is approximately 20 years).
    • -out SelfSignedCA.pem: The output filename for your CA root certificate.
    • -subj "/C=XX/ST=YourState/L=YourCity/O=Your Organization/OU=Your Unit/CN=Your Custom Root CA": Sets the subject information for the CA certificate.
      • C: Country Code (e.g., US)
      • ST: State or Province
      • L: Locality (City)
      • O: Organization Name
      • OU: Organizational Unit (e.g., IT Department)
      • CN: Common Name (The name of your CA, e.g., “My Company Internal Root CA”)
    • -config openssl_ca.cnf: Specifies the configuration file created in Step 2.

    Step 4: Generate a Server Private Key (server.key)

    Now, let’s create a private key for the server/application for which you want to enable HTTPS. This key is distinct from the CA’s private key.

    openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048

    Command explanation:

    • openssl genpkey: Same command as before to generate a private key.
    • -algorithm RSA: Using RSA algorithm.
    • -out server.key: The output filename for the server’s private key. You might want to name this more descriptively, like your-service.com.key.
    • -pkeyopt rsa_keygen_bits:2048: A 2048-bit key is generally sufficient for server keys.

    Note: For better organization, you might place server-specific files in a subdirectory, e.g., mkdir my-service && openssl genpkey -algorithm RSA -out my-service/server.key ...

    Step 5: Prepare the Server Configuration File (openssl_server.cnf)

    Before generating the server’s Certificate Signing Request (CSR), you need another OpenSSL configuration file. This file will define extensions for the CSR and the final server certificate, such as Subject Alternative Names (SANs). Create a file named openssl_server.cnf in your working directory with the following content. Remember to customize the [alt_names] section for your specific server.

    # openssl_server.cnf
    [ req ]
    distinguished_name = req_distinguished_name
    req_extensions = v3_req # Extensions for the CSR
    
    [ req_distinguished_name ]
    # No specific entries needed here if providing subject via -subj command line argument
    
    [ v3_req ]
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names # Crucial: Used by modern browsers to validate hostnames
    
    [ alt_names ]
    DNS.1 = your.domain.com       # Your server's primary domain name (should match CN)
    DNS.2 = www.your.domain.com   # Optional: Another DNS name
    DNS.3 = *.your.internal.lan # Optional: Wildcard domain for internal use
    IP.1 = 192.168.1.10           # Optional: Server's IP address, if accessed directly via IP
    # You can have more DNS.x and IP.x entries as needed.
    # Replace the above DNS and IP values with your actual server details.

    Explanation of openssl_server.cnf:

    • [ req ]:
      • req_extensions = v3_req: Specifies the section (v3_req) containing extensions to be included in the Certificate Signing Request and subsequently in the certificate.
    • [ v3_req ]: Defines X.509 v3 extensions for the server certificate.
      • basicConstraints = CA:FALSE: Specifies that this certificate is not a CA and cannot be used to sign other certificates.
      • keyUsage = digitalSignature, keyEncipherment: Defines how the certificate’s public key can be used (e.g., for digital signatures and key encryption).
      • extendedKeyUsage = serverAuth: Indicates the key can be used for server authentication (i.e., for HTTPS).
      • subjectAltName = @alt_names: Extremely important. This directive points to another section (alt_names) that lists all Subject Alternative Names. Modern browsers and applications rely on SANs rather than just the Common Name (CN) for validating hostnames.
    • [ alt_names ]:
      • Customize this section carefully.
      • DNS.1 = your.domain.com: Replace your.domain.com with the actual domain name of your server. This should usually match the CN.
      • DNS.2 = www.your.domain.com: Add any other DNS names your server uses.
      • DNS.3 = *.your.internal.lan: Example of a wildcard for an internal domain.
      • IP.1 = 192.168.1.10: Replace with your server’s actual IP address if needed. Include IP addresses if the server will be accessed directly via IP.
      • You can add more DNS.x and IP.x entries as required.

    Step 6: Generate a Server Certificate Signing Request (server.csr)

    With the server private key (server.key) and openssl_server.cnf ready, create a Certificate Signing Request (CSR) for the server. The CSR contains information about the server, including its public key, and will be sent to your CA for signing.

    openssl req -new -key server.key -out server.csr \
        -subj "/C=XX/ST=YourState/L=YourCity/O=Your Organization/CN=your.domain.com" \
        -config openssl_server.cnf

    Command explanation:

    • openssl req -new: Indicates a new CSR.
    • -key server.key: Specifies the server’s private key generated in Step 4.
    • -out server.csr: The output filename for the CSR.
    • -subj "/C=XX/ST=YourState/L=YourCity/O=Your Organization/CN=your.domain.com": Sets the subject information for the server certificate.
      • The CN (Common Name) is very important: it must match the primary domain name specified as DNS.1 in your openssl_server.cnf [alt_names] section.
    • -config openssl_server.cnf: Specifies the server-specific OpenSSL configuration file created in Step 5.

    Step 7: Sign the Server Certificate using Your CA (server.crt)

    Finally, use your CA’s private key (SelfSignedCA.key) and root certificate (SelfSignedCA.pem) to sign the server’s CSR (server.csr), thereby creating the server’s SSL/TLS certificate. This step also uses the openssl_server.cnf file for applying the necessary extensions.

    openssl x509 -req -in server.csr \
        -CA SelfSignedCA.pem -CAkey SelfSignedCA.key -CAcreateserial \
        -out server.crt -days 3650 -sha256 \
        -extfile openssl_server.cnf -extensions v3_req

    Command explanation:

    • openssl x509 -req: Command for X.509 certificate data management. The -req flag indicates that we are processing a CSR.
    • -in server.csr: Specifies the input CSR file from Step 6.
    • -CA SelfSignedCA.pem: Specifies the CA’s root certificate from Step 3.
    • -CAkey SelfSignedCA.key: Specifies the CA’s private key from Step 1. You will be prompted for the passphrase of this key if it’s encrypted.
    • -CAcreateserial: Creates and manages a serial number file (e.g., SelfSignedCA.srl). This file is necessary for the CA to keep track of issued certificates and ensure each has a unique serial number. If the file doesn’t exist, it will be created.
    • -out server.crt: The output filename for the signed server certificate.
    • -days 3650: Sets the validity period of the server certificate (e.g., 10 years). This should typically be shorter than or equal to the CA’s validity.
    • -sha256: Uses SHA-256 for the certificate signature.
    • -extfile openssl_server.cnf: Points to the OpenSSL configuration file (created in Step 5) containing certificate extensions for the server certificate.
    • -extensions v3_req: Specifies the section (v3_req) in the openssl_server.cnf configuration file that contains the X.509 v3 extensions to add to the certificate. This is where SANs, key usage, etc., are defined.

    Using Your Certificates

    1. Server Configuration: Configure your web server (e.g., Apache, Nginx) or application to use the generated server.crt (your server’s certificate) and server.key (your server’s private key) files for HTTPS.
    2. Client Trust: For clients (browsers, other applications) to trust your new server certificate without warnings, they must trust your CA. You will need to import the SelfSignedCA.pem (your CA’s root certificate) into the trust store of any system or browser that needs to connect to your server. The steps for this vary depending on the operating system and browser.

    You now have the foundation to create and manage your own certificates for internal or development use! Remember to keep your CA private key (SelfSignedCA.key) extremely secure.

  • How to Self-Host Nextcloud with Docker Compose on Ubuntu

    Last Updated: May 17, 2025

    In this post, I’ll show you how to use Docker and its Compose plugin to host your own Nextcloud service. We’ll begin with a clean Ubuntu server environment.

    Installing Docker using the Official Script

    First, ensure that curl is installed on your server.

    sudo apt update
    sudo apt install curl

    Next, we’ll install Docker using the official installation script.

    curl -fsSL https://get.docker.com -o get-docker.sh
    sh get-docker.sh

    To manage Docker as a non-root user (without needing sudo), add your current user to the docker group

    # sudo groupadd docker
    # it should be done by the script
    
    sudo usermod -aG docker $USER
    newgrp docker

    Install Nextcloud

    First, create a directory to store your Nextcloud data.

    mkdir nextcloud
    cd nextcloud

    Next, create a docker-compose.yml file. Populate it with the following configuration:

    services:
      nextcloud-database:
        image: mariadb
        container_name: nextcloud-database
        restart: always
        volumes:
          - ./database:/var/lib/mysql
        environment:
          - MYSQL_ROOT_PASSWORD=<YOUR_PASSWORD_HERE>
          - MYSQL_PASSWORD=<YOUR_PASSWORD_HERE>
          - MYSQL_DATABASE=nextcloud
          - MYSQL_USER=nextcloud
    
      nextcloud-server:
        image: nextcloud
        container_name: nextcloud-server
        restart: always
        ports:
          - <CHOOSE_A_PORT>:80
        links:
          - nextcloud-database
        volumes:
          - ./server:/var/www/html
        environment:
          - MYSQL_PASSWORD=<YOUR_PASSWORD_HERE>
          - MYSQL_DATABASE=nextcloud
          - MYSQL_USER=nextcloud
          - MYSQL_HOST=nextcloud-database

    You need to replace <YOUR_PASSWORD_HERE> with your own strong, unique password, and change <CHOOSE_A_PORT> to the external port on which you want your Nextcloud service to be accessible.

    Finally, run the following command to start your Nextcloud service in detached mode:

    docker compose up -d

    Configuring the Service

    First, open your web browser and navigate to http://<YOUR_SERVER_IP>:<THE_PORT_YOU_CONFIGURED> (replacing <YOUR_SERVER_IP> with your server’s IP address and <THE_PORT_YOU_CONFIGURED> with the port you set in the docker-compose.yml file). Follow the on-screen instructions to set up your administrator account.

    Next, create a script to handle Nextcloud’s background jobs using cron. The content of the script should be:

    #! /bin/bash
    sudo docker exec --user www-data nextcloud-server php -f /var/www/html/cron.php

    Make the script executable using chmod (replace <YOUR_SCRIPT_NAME>.sh with your actual script filename):

    chmod +x <YOUR_SCRIPT_NAME>.sh

    Finally, edit your user’s crontab to run this script every 5 minutes (ensure you use the correct path to your script):

    crontab -e
    # Add this line
    */5 * * * * <PATH_TO_SCRIPT>/<YOUR_SCRIPT>.sh

    (Optional) If you are routing traffic to Nextcloud through a reverse proxy (which might be your own HTTP server forwarding requests), you can edit the server/config/config.php file. Adding the following trusted_proxies configuration helps Nextcloud correctly process client information. Adjust the IP ranges if necessary to include your proxy server:

    'trusted_proxies' => 
    array (
      0 => '10.0.0.0/8',
      1 => '100.64.0.0/10',
      2 => '127.0.0.0/8',
      3 => '172.16.0.0/12',
      4 => '192.168.0.0/16',
    ),

  • Hello world!

    Welcome to Zhe Yuan’s Blog! Here, I’ll share technical posts on self-hosting services, P2P networks, and proxy techniques. Alongside these technical topics, I’ll also document my life, thoughts, research, and experiences from my PhD journey at The Ohio State University.

  • Compiling OVMF for ACRN and PVE on Ubuntu 18.04 LTS for iGPU Passthrough

    Last Updated: Nov 30, 2023

    This guide provides step-by-step instructions on how to compile Open Virtual Machine Firmware (OVMF) for Project ACRN and Proxmox Virtual Environment (PVE) using an Ubuntu 18.04 LTS system. The resulting OVMF is particularly useful for scenarios requiring Intel Integrated GPU (iGPU) passthrough to virtual machines. We’ll cover the necessary dependencies and compilation commands for each target environment.

    Compile OVMF for ACRN using Ubuntu 18.04 LTS

    This section will guide you through compiling OVMF specifically for use with Project ACRN, enabling iGPU passthrough capabilities.

    Install Compile Dependencies

    First, we need to install essential packages and clone the ACPICA (ACPI Component Architecture) project, which is necessary for the build process.

    sudo apt install git build-essential curl gcc-5 g++-5 python uuid-dev nasm flex bison -y
    git clone https://github.com/acpica/acpica.git
    cd acpica
    make clean
    make
    sudo make install
    cd ..

    These commands will update your package list, install the required development tools and libraries, and then download, compile, and install the ACPICA utilities.

    Compile OVMF for ACRN

    With the dependencies in place, we can now proceed to compile OVMF for ACRN. This build will incorporate elements needed for iGPU support.

    git clone https://github.com/projectacrn/acrn-edk2.git
    mkdir -p acrn-edk2/OvmfPkg/IntelGop/
    mkdir -p acrn-edk2/OvmfPkg/Vbt/
    cp IntelGopDriver.efi acrn-edk2/OvmfPkg/IntelGop/IntelGopDriver.efi
    cp Vbt.bin acrn-edk2/OvmfPkg/Vbt/Vbt.bin
    wget https://projectacrn.github.io/latest/_static/downloads/Use-the-default-vbt-released-with-GOP-driver.patch
    wget https://projectacrn.github.io/latest/_static/downloads/Integrate-IntelGopDriver-into-OVMF.patch
    cd acrn-edk2/
    git apply ../Use-the-default-vbt-released-with-GOP-driver.patch
    git apply ../Integrate-IntelGopDriver-into-OVMF.patch
    git submodule update --init CryptoPkg/Library/OpensslLib/openssl
    
    source edksetup.sh
    make -C BaseTools
    
    vim Conf/target.txt

    During the steps above, we clone the ACRN EDK2 repository, create necessary directories for Intel Graphics Output Protocol (GOP) driver and Video BIOS Table (VBT), and copy the respective files (IntelGopDriver.efi and Vbt.bin – ensure these files are present in your working directory before running the cp commands). These components are crucial for iGPU passthrough. We then download and apply patches required for ACRN. After updating the submodules, we set up the EDK2 build environment and build the base tools.

    Next, you need to edit the Conf/target.txt file. Open it with vim or your preferred text editor and ensure the following content is present:

    ACTIVE_PLATFORM = OvmfPkg/OvmfPkgX64.dsc
    TARGET_ARCH = X64
    TOOL_CHAIN_TAG = GCC5

    Finally, build OVMF with the specified options:

    build -DFD_SIZE_2MB -DDEBUG_ON_SERIAL_PORT=TRUE

    Compile OVMF for PVE

    This section details the process for compiling OVMF tailored for a Proxmox VE setup, specifically for enabling iGPU passthrough. This method utilizes Docker for a containerized build environment.

    Install Compile Dependencies (Docker)

    The primary dependency for this build method is Docker. The following commands will download and execute the official Docker installation script.

    curl -fsSL https://get.docker.com -o get-docker.sh
    sh get-docker.sh

    This ensures you have a working Docker environment. For managing Docker as a non-root user, you might need to add your user to the docker group (often handled by the script, but can be done manually with sudo usermod -aG docker $USER and then logging out and back in or using newgrp docker).

    Compile OVMF for PVE

    Now, we’ll clone the build repository and execute the build scripts. This OVMF build is intended to support iGPU passthrough in PVE. You’ll need to have your GitHub access key added to the ssh-agent to clone the repository via SSH.

    # You need to add your github accese key to the ssh-agent
    git clone [email protected]:cmd2001/build-edk2-gvtd.git
    cd build-edk2-gvtd
    sh ./init_edk2.sh
    mkdir gop
    cp ../IntelGopDriver.efi gop/IntelGopDriver.efi
    sudo bash ./build_ovmf.sh
    sudo bash ./build_oprom.sh

    These commands clone the build-edk2-gvtd repository, initialize the EDK2 environment using a script, create a directory for the GOP driver, and copy your IntelGopDriver.efi file into it (ensure this file is located in the parent directory before running cp). The IntelGopDriver.efi is key for iGPU functionality. Finally, it runs the provided shell scripts to build OVMF and the option ROM using Docker. The sudo prefix is used as the scripts likely perform operations requiring root privileges within the Docker environment or for file system manipulation.

    Following these instructions should result in successfully compiled OVMF images for either ACRN or PVE environments, configured to support iGPU passthrough.