-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathdocker_helpers.go
More file actions
131 lines (111 loc) · 4.62 KB
/
docker_helpers.go
File metadata and controls
131 lines (111 loc) · 4.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package config
import (
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"github.com/github/gh-aw-mcpg/internal/logger"
)
var logDockerHelpers = logger.New("config:docker_helpers")
// containerIDPattern validates that a container ID only contains valid characters (hex digits)
// Container IDs are 64 character hex strings, but short form (12 chars) is also valid
var containerIDPattern = regexp.MustCompile(`^[a-f0-9]{12,64}$`)
// checkDockerAccessible verifies that the Docker daemon is accessible
func checkDockerAccessible() bool {
// First check if the Docker socket exists
socketPath := os.Getenv("DOCKER_HOST")
if socketPath == "" {
socketPath = "/var/run/docker.sock"
} else {
// Parse unix:// prefix if present
socketPath = strings.TrimPrefix(socketPath, "unix://")
}
logEnv.Printf("Checking Docker socket accessibility: socketPath=%s", socketPath)
if _, err := os.Stat(socketPath); os.IsNotExist(err) {
logEnv.Printf("Docker socket not found: socketPath=%s", socketPath)
return false
}
// Try to run docker info to verify connectivity
cmd := exec.Command("docker", "info")
cmd.Stdout = nil
cmd.Stderr = nil
accessible := cmd.Run() == nil
logEnv.Printf("Docker daemon check: accessible=%v", accessible)
return accessible
}
// validateContainerID validates that the container ID is safe to use in commands
// Container IDs should only contain lowercase hex characters (a-f, 0-9)
func validateContainerID(containerID string) error {
if containerID == "" {
return fmt.Errorf("container ID is empty")
}
if !containerIDPattern.MatchString(containerID) {
return fmt.Errorf("container ID contains invalid characters: must be 12-64 hex characters")
}
return nil
}
// runDockerInspect is a helper function that executes docker inspect with a given format template.
// It validates the container ID before running the command and returns the output as a string.
//
// Security Note: This is an internal helper function that should only be called with
// hardcoded format templates defined within this package. The formatTemplate parameter
// is not validated as it is never exposed to user input.
//
// Parameters:
// - containerID: The Docker container ID to inspect (validated before use)
// - formatTemplate: The Go template format string for docker inspect (e.g., "{{.Config.OpenStdin}}")
//
// Returns:
// - output: The trimmed output from docker inspect
// - error: Any validation or command execution error
func runDockerInspect(containerID, formatTemplate string) (string, error) {
if err := validateContainerID(containerID); err != nil {
return "", err
}
logDockerHelpers.Printf("Running docker inspect: containerID=%s, format=%s", containerID, formatTemplate)
cmd := exec.Command("docker", "inspect", "--format", formatTemplate, containerID)
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("docker inspect failed: %w", err)
}
result := strings.TrimSpace(string(output))
logDockerHelpers.Printf("Docker inspect succeeded: output_len=%d", len(result))
return result, nil
}
// checkPortMapping uses docker inspect to verify that the specified port is mapped
func checkPortMapping(containerID, port string) (bool, error) {
logDockerHelpers.Printf("Checking port mapping: containerID=%s, port=%s", containerID, port)
output, err := runDockerInspect(containerID, "{{json .NetworkSettings.Ports}}")
if err != nil {
return false, err
}
// Parse the port from the output
portKey := fmt.Sprintf("%s/tcp", port)
// Check if the port is in the output with a host binding
// The format is like: {"8000/tcp":[{"HostIp":"0.0.0.0","HostPort":"8000"}]}
mapped := strings.Contains(output, portKey) && strings.Contains(output, "HostPort")
logDockerHelpers.Printf("Port mapping check result: port=%s, mapped=%v", portKey, mapped)
return mapped, nil
}
// checkStdinInteractive uses docker inspect to verify the container was started with -i flag
func checkStdinInteractive(containerID string) bool {
output, err := runDockerInspect(containerID, "{{.Config.OpenStdin}}")
if err != nil {
return false
}
interactive := output == "true"
logDockerHelpers.Printf("Stdin interactive check: containerID=%s, interactive=%v", containerID, interactive)
return interactive
}
// checkLogDirMounted uses docker inspect to verify the log directory is mounted
func checkLogDirMounted(containerID, logDir string) bool {
output, err := runDockerInspect(containerID, "{{json .Mounts}}")
if err != nil {
return false
}
// Check if the log directory is in the mounts
mounted := strings.Contains(output, logDir)
logDockerHelpers.Printf("Log dir mount check: containerID=%s, logDir=%s, mounted=%v", containerID, logDir, mounted)
return mounted
}