mirror of
https://github.com/fosrl/newt.git
synced 2026-02-08 05:56:40 +00:00
Merge pull request #46 from improbableone/hub-dev
🐳 Add Docker Socket Integration
This commit is contained in:
166
docker/client.go
Normal file
166
docker/client.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/fosrl/newt/logger"
|
||||
)
|
||||
|
||||
// Container represents a Docker container
|
||||
type Container struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Image string `json:"image"`
|
||||
State string `json:"state"`
|
||||
Status string `json:"status"`
|
||||
Ports []Port `json:"ports"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Created int64 `json:"created"`
|
||||
Networks map[string]Network `json:"networks"`
|
||||
}
|
||||
|
||||
// Port represents a port mapping for a Docker container
|
||||
type Port struct {
|
||||
PrivatePort int `json:"privatePort"`
|
||||
PublicPort int `json:"publicPort,omitempty"`
|
||||
Type string `json:"type"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
}
|
||||
|
||||
// Network represents network information for a Docker container
|
||||
type Network struct {
|
||||
NetworkID string `json:"networkId"`
|
||||
EndpointID string `json:"endpointId"`
|
||||
Gateway string `json:"gateway,omitempty"`
|
||||
IPAddress string `json:"ipAddress,omitempty"`
|
||||
IPPrefixLen int `json:"ipPrefixLen,omitempty"`
|
||||
IPv6Gateway string `json:"ipv6Gateway,omitempty"`
|
||||
GlobalIPv6Address string `json:"globalIPv6Address,omitempty"`
|
||||
GlobalIPv6PrefixLen int `json:"globalIPv6PrefixLen,omitempty"`
|
||||
MacAddress string `json:"macAddress,omitempty"`
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
DNSNames []string `json:"dnsNames,omitempty"`
|
||||
}
|
||||
|
||||
// CheckSocket checks if Docker socket is available
|
||||
func CheckSocket(socketPath string) bool {
|
||||
// Use the provided socket path or default to standard location
|
||||
if socketPath == "" {
|
||||
socketPath = "/var/run/docker.sock"
|
||||
}
|
||||
|
||||
// Try to create a connection to the Docker socket
|
||||
conn, err := net.Dial("unix", socketPath)
|
||||
if err != nil {
|
||||
logger.Debug("Docker socket not available at %s: %v", socketPath, err)
|
||||
return false
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
logger.Debug("Docker socket is available at %s", socketPath)
|
||||
return true
|
||||
}
|
||||
|
||||
// ListContainers lists all Docker containers with their network information
|
||||
func ListContainers(socketPath string) ([]Container, error) {
|
||||
// Use the provided socket path or default to standard location
|
||||
if socketPath == "" {
|
||||
socketPath = "/var/run/docker.sock"
|
||||
}
|
||||
|
||||
// Create a new Docker client
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Create client with custom socket path
|
||||
cli, err := client.NewClientWithOpts(
|
||||
client.WithHost("unix://"+socketPath),
|
||||
client.WithAPIVersionNegotiation(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Docker client: %v", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
// List containers
|
||||
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list containers: %v", err)
|
||||
}
|
||||
|
||||
var dockerContainers []Container
|
||||
for _, c := range containers {
|
||||
// Convert ports
|
||||
var ports []Port
|
||||
for _, port := range c.Ports {
|
||||
dockerPort := Port{
|
||||
PrivatePort: int(port.PrivatePort),
|
||||
Type: port.Type,
|
||||
}
|
||||
if port.PublicPort != 0 {
|
||||
dockerPort.PublicPort = int(port.PublicPort)
|
||||
}
|
||||
if port.IP != "" {
|
||||
dockerPort.IP = port.IP
|
||||
}
|
||||
ports = append(ports, dockerPort)
|
||||
}
|
||||
|
||||
// Get container name (remove leading slash)
|
||||
name := ""
|
||||
if len(c.Names) > 0 {
|
||||
name = strings.TrimPrefix(c.Names[0], "/")
|
||||
}
|
||||
|
||||
// Get network information by inspecting the container
|
||||
networks := make(map[string]Network)
|
||||
|
||||
// Inspect container to get detailed network information
|
||||
containerInfo, err := cli.ContainerInspect(ctx, c.ID)
|
||||
if err != nil {
|
||||
logger.Debug("Failed to inspect container %s for network info: %v", c.ID[:12], err)
|
||||
// Continue without network info if inspection fails
|
||||
} else {
|
||||
// Extract network information from inspection
|
||||
if containerInfo.NetworkSettings != nil && containerInfo.NetworkSettings.Networks != nil {
|
||||
for networkName, endpoint := range containerInfo.NetworkSettings.Networks {
|
||||
dockerNetwork := Network{
|
||||
NetworkID: endpoint.NetworkID,
|
||||
EndpointID: endpoint.EndpointID,
|
||||
Gateway: endpoint.Gateway,
|
||||
IPAddress: endpoint.IPAddress,
|
||||
IPPrefixLen: endpoint.IPPrefixLen,
|
||||
IPv6Gateway: endpoint.IPv6Gateway,
|
||||
GlobalIPv6Address: endpoint.GlobalIPv6Address,
|
||||
GlobalIPv6PrefixLen: endpoint.GlobalIPv6PrefixLen,
|
||||
MacAddress: endpoint.MacAddress,
|
||||
Aliases: endpoint.Aliases,
|
||||
DNSNames: endpoint.DNSNames,
|
||||
}
|
||||
networks[networkName] = dockerNetwork
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dockerContainer := Container{
|
||||
ID: c.ID[:12], // Show short ID like docker ps
|
||||
Name: name,
|
||||
Image: c.Image,
|
||||
State: c.State,
|
||||
Status: c.Status,
|
||||
Ports: ports,
|
||||
Labels: c.Labels,
|
||||
Created: c.Created,
|
||||
Networks: networks,
|
||||
}
|
||||
dockerContainers = append(dockerContainers, dockerContainer)
|
||||
}
|
||||
|
||||
return dockerContainers, nil
|
||||
}
|
||||
55
main.go
55
main.go
@@ -18,6 +18,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/fosrl/newt/docker"
|
||||
"github.com/fosrl/newt/logger"
|
||||
"github.com/fosrl/newt/proxy"
|
||||
"github.com/fosrl/newt/websocket"
|
||||
@@ -55,7 +56,7 @@ func fixKey(key string) string {
|
||||
// Decode from base64
|
||||
decoded, err := base64.StdEncoding.DecodeString(key)
|
||||
if err != nil {
|
||||
logger.Fatal("Error decoding base64:", err)
|
||||
logger.Fatal("Error decoding base64: %v", err)
|
||||
}
|
||||
|
||||
// Convert to hex
|
||||
@@ -194,7 +195,7 @@ func monitorConnectionStatus(tnet *netstack.Net, serverIP string, client *websoc
|
||||
|
||||
// Tell the server we're back
|
||||
err := client.SendMessage("newt/wg/register", map[string]interface{}{
|
||||
"publicKey": fmt.Sprintf("%s", privateKey.PublicKey()),
|
||||
"publicKey": privateKey.PublicKey().String(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -351,6 +352,7 @@ var (
|
||||
logLevel string
|
||||
updownScript string
|
||||
tlsPrivateKey string
|
||||
dockerSocket string
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -363,6 +365,7 @@ func main() {
|
||||
logLevel = os.Getenv("LOG_LEVEL")
|
||||
updownScript = os.Getenv("UPDOWN_SCRIPT")
|
||||
tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT")
|
||||
dockerSocket = os.Getenv("DOCKER_SOCKET")
|
||||
|
||||
if endpoint == "" {
|
||||
flag.StringVar(&endpoint, "endpoint", "", "Endpoint of your pangolin server")
|
||||
@@ -388,6 +391,9 @@ func main() {
|
||||
if tlsPrivateKey == "" {
|
||||
flag.StringVar(&tlsPrivateKey, "tls-client-cert", "", "Path to client certificate used for mTLS")
|
||||
}
|
||||
if dockerSocket == "" {
|
||||
flag.StringVar(&dockerSocket, "docker-socket", "/var/run/docker.sock", "Path to Docker socket")
|
||||
}
|
||||
|
||||
// do a --version check
|
||||
version := flag.Bool("version", false, "Print the version")
|
||||
@@ -498,7 +504,7 @@ func main() {
|
||||
public_key=%s
|
||||
allowed_ip=%s/32
|
||||
endpoint=%s
|
||||
persistent_keepalive_interval=5`, fixKey(fmt.Sprintf("%s", privateKey)), fixKey(wgData.PublicKey), wgData.ServerIP, endpoint)
|
||||
persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.PublicKey), wgData.ServerIP, endpoint)
|
||||
|
||||
err = dev.IpcSet(config)
|
||||
if err != nil {
|
||||
@@ -626,12 +632,53 @@ persistent_keepalive_interval=5`, fixKey(fmt.Sprintf("%s", privateKey)), fixKey(
|
||||
}
|
||||
})
|
||||
|
||||
// Register handler for Docker socket check
|
||||
client.RegisterHandler("newt/socket/check", func(msg websocket.WSMessage) {
|
||||
logger.Info("Received Docker socket check request")
|
||||
|
||||
// Check if Docker socket is available
|
||||
isAvailable := docker.CheckSocket(dockerSocket)
|
||||
|
||||
// Send response back to server
|
||||
err := client.SendMessage("newt/socket/status", map[string]interface{}{
|
||||
"available": isAvailable,
|
||||
"socketPath": dockerSocket,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("Failed to send Docker socket check response: %v", err)
|
||||
} else {
|
||||
logger.Info("Docker socket check response sent: available=%t", isAvailable)
|
||||
}
|
||||
})
|
||||
|
||||
// Register handler for Docker container listing
|
||||
client.RegisterHandler("newt/socket/fetch", func(msg websocket.WSMessage) {
|
||||
logger.Info("Received Docker container fetch request")
|
||||
|
||||
// List Docker containers
|
||||
containers, err := docker.ListContainers(dockerSocket)
|
||||
if err != nil {
|
||||
logger.Error("Failed to list Docker containers: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Send container list back to server
|
||||
err = client.SendMessage("newt/socket/containers", map[string]interface{}{
|
||||
"containers": containers,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("Failed to send Docker container list: %v", err)
|
||||
} else {
|
||||
logger.Info("Docker container list sent, count: %d", len(containers))
|
||||
}
|
||||
})
|
||||
|
||||
client.OnConnect(func() error {
|
||||
publicKey := privateKey.PublicKey()
|
||||
logger.Debug("Public key: %s", publicKey)
|
||||
|
||||
err := client.SendMessage("newt/wg/register", map[string]interface{}{
|
||||
"publicKey": fmt.Sprintf("%s", publicKey),
|
||||
"publicKey": publicKey.String(),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("Failed to send registration message: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user