Skip to content

Ensure a smoother GPG key update process for September 2026 expiration #9572

Open

Description

@williammartin

Description

In #9569, we detail the expiration of the GPG key used to sign our .deb and .rpm package repository contents. With the late notice, we were limited in the options available to us, choosing to extend the expiration date of the existing key. Unfortunately, all our users need to obtain this new key through some mechanism, and this is likely to be an inconvenient process for many.

In #6856 there was a suggested approach to avoiding this issue in the future by packaging the key inside the .deb, so that as users updated their version of the CLI, with enough advanced notice, they would hopefully already have the new key long before their expiration. Another approach discussed internally is to package the key separately and to make that a dependency of gh. Thus, as users ran apt update, with enough advanced notice, they would hopefully already have the new key long before their expiration.

The approaches above were primarily focused on our .deb repository. We should ensure that any solution is also relevant for our .rpm repository.

We should decide an approach to follow up on fairly quickly, such that this doesn't get lost through team changes or in the myriad other work.

If anyone in the community has experience with managing GPG keys for self-hosted repositories, we'd love to hear from you on this issue.

Expected Output

The expected output of this issue is:

  • A document in this repository outlining the options and recommending one
  • A comment closing out this issue pointing to that document
  • A new issue in this repository capturing the recommended work

Activity

  1. sftim commented on Sep 7, 2024

    @sftim

    If anyone in the community has experience with managing GPG keys for self-hosted repositories, we'd love to hear from you on this issue.

    You're very welcome to get in touch with @scalefactory - this is something we can help you manage, albeit it's usually not gratis help if you're a commercial operation.

  2. williammartin commented on Sep 16, 2024

    @williammartin
    MemberAuthor

    Just capturing a script I cobbled together between ChatGPT and this Earthly Blog Post (thanks @alexcb). It's a starting point to create an apt repository that serves a golang binary. Later I will enhance it to handle signing to act as a baseline for exploring this piece of work.

    https://gist.github.com/williammartin/8ed21a08254eb764129e8437a89b815a

    Details
    #!/bin/bash
    
    # Enable strict mode
    set -e
    
    # Check if DEBUG is set to true or 1, enable debug print statements if so
    if [[ "$DEBUG" == "true" || "$DEBUG" == "1" ]]; then
      set -x
    fi
    
    # Default artifacts directory
    DEFAULT_ARTIFACTS_DIR="$(pwd)/deb-repo-test"
    
    # Function to prompt user to continue to the next step with explanations
    prompt_continue() {
      echo
      read -p "$1 Press Enter to continue..."
    }
    
    # Function to prompt for the artifacts directory and ensure it exists
    prompt_for_artifacts_dir() {
      read -p "Please provide a directory location for storing all artifacts (leave empty for default: $DEFAULT_ARTIFACTS_DIR): "
      # Use default if the user provides no input
      if [ -z "$ARTIFACTS_DIR" ]; then
        ARTIFACTS_DIR="$DEFAULT_ARTIFACTS_DIR"
        echo "No directory provided. Using default: $ARTIFACTS_DIR"
      fi
    }
    
    # Function to handle the setup process
    setup() {
      # Step 1: Install prerequisites (including Go)
      echo "Step 1: Installing prerequisites (Go, dpkg-dev, gpg, tree)"
      sudo apt-get install -y golang-go dpkg-dev gpg tree
      prompt_continue "Next, we will ask you to provide a directory location to store all artifacts."
    
      # Prompt for the artifacts directory
      prompt_for_artifacts_dir
    
      # Check if directory exists, if not, create it
      if [ ! -d "$ARTIFACTS_DIR" ]; then
        echo "Directory does not exist. Creating $ARTIFACTS_DIR..."
        mkdir -p "$ARTIFACTS_DIR"
        echo "Directory $ARTIFACTS_DIR created."
      else
        echo "Directory $ARTIFACTS_DIR already exists."
      fi
      tree "$ARTIFACTS_DIR"
      prompt_continue "Next, we will create a simple Go program within the specified directory."
    
      # Step 2: Create Go Program
      echo "Step 2: Creating a simple Go program"
      mkdir -p "$ARTIFACTS_DIR/hello-world-program"
      cat <<EOF > "$ARTIFACTS_DIR/hello-world-program/main.go"
    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("hello statically compiled world")
    }
    EOF
    
      # Step 3: Compile the Go program statically
      echo "Step 3: Compiling the Go program statically"
      cd "$ARTIFACTS_DIR/hello-world-program"
      CGO_ENABLED=0 go build -o hello-world -ldflags="-extldflags=-static" main.go
      echo "Go program compiled as a static binary."
      tree "$ARTIFACTS_DIR"
      prompt_continue "Next, we will create the directory structure for the deb package."
    
      # Step 4: Create directory structure for deb package
      echo "Step 4: Creating directory structure for the deb package"
      mkdir -p "$ARTIFACTS_DIR/hello-world_0.0.1-1_arm64"
      cd "$ARTIFACTS_DIR/hello-world_0.0.1-1_arm64"
      mkdir -p usr/bin
      cp "$ARTIFACTS_DIR/hello-world-program/hello-world" usr/bin/
      echo "Binary copied to usr/bin/hello-world."
      tree "$ARTIFACTS_DIR"
      prompt_continue "Next, we will create the necessary control file for the deb package."
    
      # Step 5: Create DEBIAN control file
      echo "Step 5: Creating DEBIAN control file"
      mkdir -p DEBIAN
      echo "Package: hello-world
    Version: 0.0.1
    Maintainer: example <[email protected]>
    Architecture: arm64
    Homepage: http://example.com
    Description: A statically compiled Go program that prints hello" > DEBIAN/control
      echo "Control file created at $ARTIFACTS_DIR/hello-world_0.0.1-1_arm64/DEBIAN/control."
      tree "$ARTIFACTS_DIR"
      prompt_continue "Next, we will build the .deb package from the files we have set up."
    
      # Step 6: Build the deb package
      echo "Step 6: Building the .deb package"
      cd "$ARTIFACTS_DIR"
      dpkg --build hello-world_0.0.1-1_arm64
      echo "Deb package created: hello-world_0.0.1-1_arm64.deb"
      tree "$ARTIFACTS_DIR"
      prompt_continue "Next, we will inspect the .deb package to verify its contents."
    
      # Step 7: Inspect the deb package
      echo "Step 7: Inspecting the .deb package"
      dpkg-deb --info hello-world_0.0.1-1_arm64.deb
      dpkg-deb --contents hello-world_0.0.1-1_arm64.deb
      prompt_continue "Next, we will set up a directory structure for hosting this package in an apt repository."
    
      # Step 8: Create apt repository directory structure
      echo "Step 8: Creating apt repository directory structure"
      mkdir -p "$ARTIFACTS_DIR/apt-repo/pool/main/"
      cp "$ARTIFACTS_DIR/hello-world_0.0.1-1_arm64.deb" "$ARTIFACTS_DIR/apt-repo/pool/main/"
      mkdir -p "$ARTIFACTS_DIR/apt-repo/dists/stable/main/binary-arm64"
      tree "$ARTIFACTS_DIR"
      prompt_continue "Next, we will generate the 'Packages' file, which lists the available deb packages in the repository."
    
      # Step 9: Generate Packages file
      echo "Step 9: Generating Packages file"
      cd "$ARTIFACTS_DIR/apt-repo"
      dpkg-scanpackages --arch arm64 pool/ > dists/stable/main/binary-arm64/Packages
      cat dists/stable/main/binary-arm64/Packages | gzip -9 > dists/stable/main/binary-arm64/Packages.gz
      tree "$ARTIFACTS_DIR"
      prompt_continue "Next, we will create a Release file, which is required by apt repositories."
      
      # Step 10: Create Release file with a bash script (with debugging logs to stderr)
      echo "Step 10: Generating Release file using bash script with detailed debugging"
      cat <<EOF > "$ARTIFACTS_DIR/generate-release.sh"
    #!/bin/bash
    set -e
    
    # Enable debug output if DEBUG is set to true or 1
    DEBUG="\${DEBUG:-false}"
    
    # Debug function to print messages to stderr if DEBUG is enabled
    debug_log() {
        if [[ "\$DEBUG" == "true" || "\$DEBUG" == "1" ]]; then
            echo "[DEBUG] \$1" >&2
        fi
    }
    
    # Function to handle hashing with more detailed debugging
    do_hash() {
        HASH_NAME=\$1
        HASH_CMD=\$2
        echo "\${HASH_NAME}:"
    
        # Find all files and remove the './' prefix
        for f in \$(find . -type f); do
            f=\$(echo "\$f" | cut -c3-)
    
            # Skip the Release file itself
            if [ "\$f" = "Release" ]; then
                debug_log "Skipping the Release file: \$f"
                continue
            fi
    
            # Check file permissions and existence before hashing
            if [ -r "\$f" ]; then
                debug_log "Processing file: \$f"
                debug_log "File permissions: \$(ls -l "\$f")"
                echo " \$(\${HASH_CMD} "\$f" | cut -d' ' -f1) \$(wc -c < "\$f") \$f"
            else
                debug_log "[ERROR] Permission denied or file not readable: \$f"
                echo "[ERROR] Permission denied or file not readable: \$f" >&2
                exit 1
            fi
        done
    }
    
    # Start logging
    debug_log "Generating Release file with detailed debugging..."
    
    # Output Release file metadata
    cat <<EOF2
    Origin: Example Repository
    Label: Example
    Suite: stable
    Codename: stable
    Version: 1.0
    Architectures: arm64
    Components: main
    Description: An example software repository
    Date: \$(date -Ru)
    EOF2
    
    # Perform MD5, SHA1, and SHA256 hashes with debugging
    do_hash "MD5Sum" "md5sum"
    do_hash "SHA1" "sha1sum"
    do_hash "SHA256" "sha256sum"
    
    debug_log "Release file generation complete."
    EOF
    
      chmod +x "$ARTIFACTS_DIR/generate-release.sh"
    
      cd "$ARTIFACTS_DIR/apt-repo/dists/stable"
      "$ARTIFACTS_DIR/generate-release.sh" > Release
      echo "Release file generated."
      tree "$ARTIFACTS_DIR"
      prompt_continue "Next, we will host the repository using Python's HTTP server for testing."
    
      # Step 11: Serve the repository using a local HTTP server
      echo "Step 11: Hosting repository with Python HTTP server"
      cd "$ARTIFACTS_DIR"
      python3 -m http.server 8000 > /dev/null 2>&1 &
      echo "HTTP server started at http://127.0.0.1:8000"
      prompt_continue "Next, we will add the local repository to the system's sources list and update apt."
    
      # Step 12: Add the repository to the system's sources
      echo "Step 12: Adding repository to /etc/apt/sources.list.d"
      echo "deb [arch=arm64] http://127.0.0.1:8000/apt-repo stable main" | sudo tee /etc/apt/sources.list.d/example.list
      prompt_continue "Next, we will update apt and install the 'hello-world' package from the repository."
    
      # Step 13: Update and install the package
      echo "Step 13: Updating apt and installing the package"
      sudo apt-get update --allow-insecure-repositories
      sudo apt-get install hello-world
      echo "Hello World package installed successfully."
    }
    
    # Function to handle the teardown process
    teardown() {
      # Prompt for the artifacts directory during teardown
      prompt_for_artifacts_dir
    
      echo "Teardown: Cleaning up everything."
    
      # Kill the Python HTTP server if it's running
      if pgrep -f "python3 -m http.server" > /dev/null; then
        echo "Stopping the Python HTTP server..."
        pkill -f "python3 -m http.server"
        echo "Server stopped."
      fi
    
      # Check if the hello-world package is installed, and remove it safely
      if dpkg -l | grep -q "^ii  hello-world"; then
        echo "Removing the hello-world package..."
        sudo apt-get remove --purge -y hello-world
        echo "Package hello-world removed."
      else
        echo "Package hello-world is not installed, skipping removal."
      fi
    
      # Remove the repository from sources.list.d
      if [ -f /etc/apt/sources.list.d/example.list ]; then
        echo "Removing the repository from /etc/apt/sources.list.d..."
        sudo rm /etc/apt/sources.list.d/example.list
        echo "Repository removed."
      fi
    
      # Remove the artifacts directory
      if [ -d "$ARTIFACTS_DIR" ]; then
        echo "No directory provided. Using default: $ARTIFACTS_DIR"
        echo "Removing the artifacts directory: $ARTIFACTS_DIR"
        rm -rf "$ARTIFACTS_DIR"
        echo "Artifacts directory removed."
      fi
    
      # Clean apt cache
      echo "Cleaning apt cache..."
      sudo apt-get clean
      echo "Cleanup complete."
    }
    
    # Check for the setup or teardown argument
    if [ "$1" == "setup" ]; then
      setup
    elif [ "$1" == "teardown" ]; then
      teardown
    else
      echo "Usage: $0 {setup|teardown}"
      exit 1
    fi
  3. T4hob commented on Sep 27, 2024

    @T4hob
  4. j3262049232 commented on Nov 3, 2024

    @j3262049232
  5. added a commit that references this issue on Aug 8, 2025
  6. kharakhararonakronakkhara-ai commented on Aug 9, 2025

    @kharakhararonakronakkhara-ai
  7. added a commit that references this issue on Sep 12, 2025
  8. github-actions commented on Feb 17, 2026

    @github-actions
    Contributor

    Thank you for your issue! We have categorized it as an enhancement (feature or improvement) request, and it has been added to our backlog. In doing so, we are not committing to implementing this feature at this time, but, we will consider it for future releases based on community feedback and our own product roadmap.

    Unless you see the help wanted Contributions welcome label, we are not currently looking for external contributions for this feature.

    If you come across this issue and would like to see it implemented, please add a thumbs up! This will help us prioritize the feature. Please only comment if you have additional information or viewpoints to contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementa request to improve CLIpackagingtech-debtA chore that addresses technical debt

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions