mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-17 23:03:49 +01:00
feat(socket-proxy): implement Docker socket proxy and related configurations
- Updated Dockerfile and Makefile for socket-proxy build. - Modified go.mod to include necessary dependencies. - Updated CI workflows for socket-proxy integration. - Better module isolation - Code refactor
This commit is contained in:
179
socket-proxy/pkg/handler.go
Normal file
179
socket-proxy/pkg/handler.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package socketproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var dialer = &net.Dialer{KeepAlive: 1 * time.Second}
|
||||
|
||||
func dialDockerSocket(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, "unix", DockerSocket)
|
||||
}
|
||||
|
||||
var DockerSocketHandler = dockerSocketHandler
|
||||
|
||||
func dockerSocketHandler() http.HandlerFunc {
|
||||
rp := httputil.NewSingleHostReverseProxy(&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "api.moby.localhost",
|
||||
})
|
||||
rp.Transport = &http.Transport{
|
||||
DialContext: dialDockerSocket,
|
||||
}
|
||||
|
||||
return rp.ServeHTTP
|
||||
}
|
||||
|
||||
func endpointNotAllowed(w http.ResponseWriter, _ *http.Request) {
|
||||
http.Error(w, "Endpoint not allowed", http.StatusForbidden)
|
||||
}
|
||||
|
||||
// ref: https://github.com/Tecnativa/docker-socket-proxy/blob/master/haproxy.cfg
|
||||
func NewHandler() http.Handler {
|
||||
r := mux.NewRouter()
|
||||
socketHandler := DockerSocketHandler()
|
||||
|
||||
const apiVersionPrefix = `/{version:(?:v[\d\.]+)?}`
|
||||
const containerPath = "/containers/{id:[a-zA-Z0-9_.-]+}"
|
||||
|
||||
allowedPaths := []string{}
|
||||
deniedPaths := []string{}
|
||||
|
||||
if DockerContainers {
|
||||
allowedPaths = append(allowedPaths, "/containers")
|
||||
if !DockerRestarts {
|
||||
deniedPaths = append(deniedPaths, containerPath+"/stop")
|
||||
deniedPaths = append(deniedPaths, containerPath+"/restart")
|
||||
deniedPaths = append(deniedPaths, containerPath+"/kill")
|
||||
}
|
||||
if !DockerStart {
|
||||
deniedPaths = append(deniedPaths, containerPath+"/start")
|
||||
}
|
||||
if !DockerStop && DockerRestarts {
|
||||
deniedPaths = append(deniedPaths, containerPath+"/stop")
|
||||
}
|
||||
}
|
||||
if DockerAuth {
|
||||
allowedPaths = append(allowedPaths, "/auth")
|
||||
}
|
||||
if DockerBuild {
|
||||
allowedPaths = append(allowedPaths, "/build")
|
||||
}
|
||||
if DockerCommit {
|
||||
allowedPaths = append(allowedPaths, "/commit")
|
||||
}
|
||||
if DockerConfigs {
|
||||
allowedPaths = append(allowedPaths, "/configs")
|
||||
}
|
||||
if DockerDistribution {
|
||||
allowedPaths = append(allowedPaths, "/distribution")
|
||||
}
|
||||
if DockerEvents {
|
||||
allowedPaths = append(allowedPaths, "/events")
|
||||
}
|
||||
if DockerExec {
|
||||
allowedPaths = append(allowedPaths, "/exec")
|
||||
}
|
||||
if DockerGrpc {
|
||||
allowedPaths = append(allowedPaths, "/grpc")
|
||||
}
|
||||
if DockerImages {
|
||||
allowedPaths = append(allowedPaths, "/images")
|
||||
}
|
||||
if DockerInfo {
|
||||
allowedPaths = append(allowedPaths, "/info")
|
||||
}
|
||||
if DockerNetworks {
|
||||
allowedPaths = append(allowedPaths, "/networks")
|
||||
}
|
||||
if DockerNodes {
|
||||
allowedPaths = append(allowedPaths, "/nodes")
|
||||
}
|
||||
if DockerPing {
|
||||
allowedPaths = append(allowedPaths, "/_ping")
|
||||
}
|
||||
if DockerPlugins {
|
||||
allowedPaths = append(allowedPaths, "/plugins")
|
||||
}
|
||||
if DockerSecrets {
|
||||
allowedPaths = append(allowedPaths, "/secrets")
|
||||
}
|
||||
if DockerServices {
|
||||
allowedPaths = append(allowedPaths, "/services")
|
||||
}
|
||||
if DockerSession {
|
||||
allowedPaths = append(allowedPaths, "/session")
|
||||
}
|
||||
if DockerSwarm {
|
||||
allowedPaths = append(allowedPaths, "/swarm")
|
||||
}
|
||||
if DockerSystem {
|
||||
allowedPaths = append(allowedPaths, "/system")
|
||||
}
|
||||
if DockerTasks {
|
||||
allowedPaths = append(allowedPaths, "/tasks")
|
||||
}
|
||||
if DockerVersion {
|
||||
allowedPaths = append(allowedPaths, "/version")
|
||||
}
|
||||
if DockerVolumes {
|
||||
allowedPaths = append(allowedPaths, "/volumes")
|
||||
}
|
||||
|
||||
// Helper to determine if a path should be treated as a prefix
|
||||
isPrefixPath := func(path string) bool {
|
||||
return strings.Count(path, "/") == 1
|
||||
}
|
||||
|
||||
// 1. Register Denied Paths (specific)
|
||||
for _, path := range deniedPaths {
|
||||
// Handle with version prefix
|
||||
r.HandleFunc(apiVersionPrefix+path, endpointNotAllowed)
|
||||
// Handle without version prefix
|
||||
r.HandleFunc(path, endpointNotAllowed)
|
||||
}
|
||||
|
||||
// 2. Register Allowed Paths
|
||||
for _, p := range allowedPaths {
|
||||
fullPathWithVersion := apiVersionPrefix + p
|
||||
if isPrefixPath(p) {
|
||||
r.PathPrefix(fullPathWithVersion).Handler(socketHandler)
|
||||
r.PathPrefix(p).Handler(socketHandler)
|
||||
} else {
|
||||
r.HandleFunc(fullPathWithVersion, socketHandler)
|
||||
r.HandleFunc(p, socketHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Add fallback for any other routes
|
||||
r.PathPrefix("/").HandlerFunc(endpointNotAllowed)
|
||||
|
||||
// HTTP method filtering
|
||||
if !DockerPost {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case http.MethodGet:
|
||||
r.ServeHTTP(w, req)
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
})
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case http.MethodPost, http.MethodGet:
|
||||
r.ServeHTTP(w, req)
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user