Skip to content

Commit

Permalink
Remove public docker listener and proxy to it in dockerproxy.
Browse files Browse the repository at this point in the history
This addresses a security issue with unauthenticated dockerd being exposed on 6pn by restricting the endpoints that are serviced to just those that are needed by flyctl's deploy builder.

- Move dockerd to localhost port 2376.
- Make dockerproxy proxy 2375 to 2376, with a list of allwed URLs.
  • Loading branch information
timflyio committed Jul 21, 2024
1 parent 9346699 commit 45b9cc6
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 9 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ http://localhost:8080 will have the rchab api in the vm and on your host.
`flyctl` can be configured to use a locally running version of rchab with:

```shell
FLY_REMOTE_BUILDER_HOST_WG=1 FLY_RCHAB_OVERRIDE_HOST=tcp://127.0.0.1:2375 LOG_LEVEL=debug fly deploy --remote-only
FLY_REMOTE_BUILDER_HOST_WG=1 FLY_RCHAB_OVERRIDE_HOST=tcp://127.0.0.1:2376 LOG_LEVEL=debug fly deploy --remote-only
```

* `FLY_REMOTE_BUILDER_HOST_WG` disables usermode wireguard
Expand Down
61 changes: 55 additions & 6 deletions dockerproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/url"
"os"
"os/signal"
"regexp"
"strings"
"sync/atomic"
"syscall"
Expand Down Expand Up @@ -48,11 +49,19 @@ var (
)

const (
DOCKER_LISTENER = "localhost:2375"
DOCKER_LISTENER = "localhost:2376"
DOCKER_SCHEME = "http"
FLY_API_URL = "https://api.fly.io"
)

var allowedPaths = []*regexp.Regexp{
regexp.MustCompile("/flyio/.*"),
regexp.MustCompile("/grpc"),
regexp.MustCompile("/_ping"),
regexp.MustCompile("^(/v[0-9.]*)?/info"),
regexp.MustCompile("^(/v[0-9.]*)?/images/.*"),
}

func init() {
api.SetBaseURL(FLY_API_URL)
}
Expand Down Expand Up @@ -105,7 +114,7 @@ func main() {

httpMux := http.NewServeMux()

httpMux.Handle("/", wrapCommonMiddlewares(dockerProxy()))
httpMux.Handle("/", wrapCommonMiddlewares(dockerProxy(nil)))
httpMux.Handle("/flyio/v1/prune", wrapCommonMiddlewares(pruneHandler(dockerClient)))
httpMux.Handle("/flyio/v1/extendDeadline", wrapCommonMiddlewares((extendDeadline())))
httpMux.Handle("/flyio/v1/buildOverlaybdImage", wrapCommonMiddlewares(overlaybdImageHandler()))
Expand All @@ -128,7 +137,28 @@ func main() {
go func() {
log.Infof("Listening on %s", httpServer.Addr)
if err := httpServer.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("failed to listenAndServe: %v", err)
log.Fatalf("failed to listenAndServe on %s: %v", httpServer.Addr, err)
}
}()

httpServer2 := &http.Server{
Addr: ":2375",
Handler: dockerProxy(allowedPaths),
BaseContext: func(_ net.Listener) context.Context {
return ctx
},

// keep these as high as possible. shorter read/write timeouts can cause push operations
// for large images to hang midway with the error -> context.Cancelled.
ReadTimeout: 15 * time.Minute,
WriteTimeout: 15 * time.Minute,
}
httpServer2.RegisterOnShutdown(cancel)

go func() {
log.Infof("Listening on %s", httpServer2.Addr)
if err := httpServer2.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("failed to listenAndServe on %s: %v", httpServer2.Addr, err)
}
}()

Expand Down Expand Up @@ -158,11 +188,17 @@ func main() {

log.Info("shutting down proxy")
if err := httpServer.Shutdown(gracefullCtx); err != nil {
log.Warnf("shutdown error: %v\n", err)
log.Warnf("shutdown error on %s: %v\n", httpServer.Addr, err)
os.Exit(1)
}

log.Info("shutting down proxy")
log.Info("shutting down proxy2")
if err := httpServer2.Shutdown(gracefullCtx); err != nil {
log.Warnf("shutdown error on %s: %v\n", httpServer2.Addr, err)
os.Exit(1)
}

log.Info("shutting down docker")
stopDockerdFn()

log.Info("shutdown complete")
Expand Down Expand Up @@ -216,7 +252,7 @@ func extendDeadline() http.Handler {
})
}

func dockerProxy() http.Handler {
func dockerProxy(allowedPaths []*regexp.Regexp) http.Handler {
reverseProxy := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: DOCKER_SCHEME,
Host: DOCKER_LISTENER,
Expand All @@ -225,6 +261,19 @@ func dockerProxy() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
pendingRequests.Add(1)

allowed := false
for _, allowedPath := range allowedPaths {
if allowedPath.MatchString(r.URL.Path) {
allowed = true
break
}
}
if allowedPaths != nil && !allowed {
log.Warnf("Refusing to proxy %s", r.URL)
http.Error(w, `{"message":"page not found"}`, http.StatusNotFound)
return
}

defer func() {
pendingRequests.Add(^uint64(0))
}()
Expand Down
4 changes: 2 additions & 2 deletions etc/docker/daemon.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
},
"hosts": [
"unix:///var/run/docker.sock",
"tcp://0.0.0.0:2375"
"tcp://127.0.0.1:2376"
],
"mtu": 1400,
"max-concurrent-downloads": 10,
Expand All @@ -27,4 +27,4 @@
"registry-mirrors": [
"https://docker-hub-mirror.fly.io"
]
}
}

0 comments on commit 45b9cc6

Please sign in to comment.