Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.9] - 2025-10-21

### Added

* Added support for Podman as an alternative to Docker (PR #22; Thanks to @hdm for the contribution)
* Podman must be configured to use [Docker compatibility mode](https://podman-desktop.io/docs/migrating-from-docker/managing-docker-compatibility)

## [0.1.8] - 2025-07-22

### Added
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

Golang code for the `bloodhound-cli` binary in [BloodHound](https://github.com/SpecterOps/BloodHound). This binary provides control for various aspects of BloodHound's configuration.

BloodHound CLI is compatible with Docker Compose v2 and Podman. If using Podman, configure [Docker compatibility mode](https://podman-desktop.io/docs/migrating-from-docker/managing-docker-compatibility).

## Usage

Execute `./bloodhound-cli help` for usage information (see below).
Expand Down
21 changes: 14 additions & 7 deletions cmd/internal/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"context"
"encoding/binary"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"log"
"os"
"path/filepath"

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)

// Vars for tracking the list of BloodHound images
Expand All @@ -20,7 +21,7 @@ var (
devImages = []string{
"bhce_bloodhound", "bhce_neo4j", "bhce_postgres",
}
// Default root command for Docker commands
// Default root command for Docker commands, will fallback to Podman if Docker is not found
dockerCmd = "docker"
// URLs for the BloodHound compose files
devYaml = "docker-compose.dev.yml"
Expand Down Expand Up @@ -65,17 +66,23 @@ func EvaluateDockerComposeStatus() {
// Check for ``docker`` first because it's required for everything to come
dockerExists := CheckPath("docker")
if !dockerExists {
log.Fatalln("Docker is not installed on this system, so please install Docker and try again.")
podmanExists := CheckPath("podman")
if podmanExists {
fmt.Println("[+] Docker is not installed, but Podman is installed. Using Podman as a Docker alternative.")
dockerCmd = "podman"
} else {
log.Fatalln("Neither Docker nor Podman is installed on this system, so please install Docker or Podman (in Docker compatibility mode) and try again.")
}
}

// Check if the Docker Engine is running
_, engineErr := RunBasicCmd("docker", []string{"info"})
_, engineErr := RunBasicCmd(dockerCmd, []string{"info"})
if engineErr != nil {
log.Fatalln("Docker is installed on this system, but the daemon is not running.")
log.Fatalf("%s is installed on this system, but the daemon is not running or access was denied.", dockerCmd)
}

// Check for the ``compose`` plugin as our first choice
_, composeErr := RunBasicCmd("docker", []string{"compose", "version"})
_, composeErr := RunBasicCmd(dockerCmd, []string{"compose", "version"})
if composeErr != nil {
fmt.Println("[+] The `compose` plugin is not installed, so we'll try the deprecated `docker-compose` script")
composeScriptExists := CheckPath("docker-compose")
Expand Down
7 changes: 4 additions & 3 deletions cmd/internal/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"bufio"
"encoding/json"
"fmt"
"github.com/adrg/xdg"
"io"
"log"
"net/http"
Expand All @@ -16,6 +15,8 @@ import (
"path/filepath"
"strings"
"time"

"github.com/adrg/xdg"
)

// HealthIssue is a custom type for storing healthcheck output.
Expand Down Expand Up @@ -164,8 +165,8 @@ func RunBasicCmd(name string, args []string) (string, error) {
// RunCmd executes a given command ("name") with a list of arguments ("args")
// and return stdout and stderr buffers.
func RunCmd(name string, args []string) error {
// If the command is ``docker``, prepend ``compose`` to the args
if name == "docker" {
// If the command is ``docker`` or ``podman``, prepend ``compose`` to the args
if name == "docker" || name == "podman" {
args = append([]string{"compose"}, args...)
}
path, err := exec.LookPath(name)
Expand Down
9 changes: 7 additions & 2 deletions cmd/internal/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package internal

import (
"github.com/stretchr/testify/assert"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestGetCwdFromExe(t *testing.T) {
Expand All @@ -12,7 +13,11 @@ func TestGetCwdFromExe(t *testing.T) {
}

func TestCheckPath(t *testing.T) {
assert.True(t, CheckPath("docker"), "Expected `CheckPath()` to find `docker` in `$PATH`")
dockerFound := CheckPath("docker")
if !dockerFound {
dockerFound = CheckPath("podman")
}
assert.True(t, dockerFound, "Expected `CheckPath()` to find `docker` or `podman` in `$PATH`")
}

func TestRunBasicCmd(t *testing.T) {
Expand Down
Loading