-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
factor out image serving #11
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package imageserver | ||
|
||
import ( | ||
docker "github.com/fsouza/go-dockerclient" | ||
) | ||
|
||
// ImageServer abstracts the serving of image information. | ||
type ImageServer interface { | ||
// ServeImage Serves the image | ||
ServeImage(imageMetadata *docker.Image) error | ||
} | ||
|
||
// APIVersions holds a slice of supported API versions. | ||
type APIVersions struct { | ||
// Versions is the supported API versions | ||
Versions []string `json:"versions"` | ||
} | ||
|
||
// ImageServerOptions is used to configure an image server. | ||
type ImageServerOptions struct { | ||
// ServePath is the root path/port of serving. ex 0.0.0.0:8080 | ||
ServePath string | ||
// HealthzURL is the relative url of the health check. ex /healthz | ||
HealthzURL string | ||
// APIURL is the relative url where the api will be served. ex /api | ||
APIURL string | ||
// APIVersions are the supported API versions. | ||
APIVersions APIVersions | ||
// MetadataURL is the relative url of the metadata content. ex /api/v1/metadata | ||
MetadataURL string | ||
// ContentURL is the relative url of the content. ex /api/v1/content/ | ||
ContentURL string | ||
// ImageServeURL is the location that the image is being served from. | ||
// NOTE: if the image server supports a chroot the server implementation will perform | ||
// the chroot based on this URL. | ||
ImageServeURL string | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package imageserver | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"syscall" | ||
|
||
"golang.org/x/net/webdav" | ||
|
||
docker "github.com/fsouza/go-dockerclient" | ||
) | ||
|
||
const ( | ||
// CHROOT_SERVE_PATH is the path to server if we are performing a chroot | ||
// this probably does not belong here. | ||
CHROOT_SERVE_PATH = "/" | ||
) | ||
|
||
// webdavImageServer implements ImageServer. | ||
type webdavImageServer struct { | ||
opts ImageServerOptions | ||
chroot bool | ||
} | ||
|
||
// ensures this always implements the interface or fail compilation. | ||
var _ ImageServer = &webdavImageServer{} | ||
|
||
// NewWebdavImageServer creates a new webdav image server. | ||
func NewWebdavImageServer(opts ImageServerOptions, chroot bool) ImageServer { | ||
return &webdavImageServer{ | ||
opts: opts, | ||
chroot: chroot, | ||
} | ||
} | ||
|
||
// ServeImage Serves the image. | ||
func (s *webdavImageServer) ServeImage(imageMetadata *docker.Image) error { | ||
servePath := s.opts.ImageServeURL | ||
if s.chroot { | ||
if err := syscall.Chroot(s.opts.ImageServeURL); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pweil- we have a problem here: In the |
||
return fmt.Errorf("Unable to chroot into %s: %v\n", s.opts.ImageServeURL, err) | ||
} | ||
servePath = CHROOT_SERVE_PATH | ||
} else { | ||
log.Printf("!!!WARNING!!! It is insecure to serve the image content without changing") | ||
log.Printf("root (--chroot). Absolute-path symlinks in the image can lead to disclose") | ||
log.Printf("information of the hosting system.") | ||
} | ||
|
||
log.Printf("Serving image content %s on webdav://%s%s", s.opts.ImageServeURL, s.opts.ServePath, s.opts.ContentURL) | ||
|
||
http.HandleFunc(s.opts.HealthzURL, func(w http.ResponseWriter, r *http.Request) { | ||
w.Write([]byte("ok\n")) | ||
}) | ||
|
||
http.HandleFunc(s.opts.APIURL, func(w http.ResponseWriter, r *http.Request) { | ||
body, err := json.MarshalIndent(s.opts.APIVersions, "", " ") | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
w.Write(body) | ||
}) | ||
|
||
http.HandleFunc(s.opts.MetadataURL, func(w http.ResponseWriter, r *http.Request) { | ||
body, err := json.MarshalIndent(imageMetadata, "", " ") | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
w.Write(body) | ||
}) | ||
|
||
http.Handle(s.opts.ContentURL, &webdav.Handler{ | ||
Prefix: s.opts.ContentURL, | ||
FileSystem: webdav.Dir(servePath), | ||
LockSystem: webdav.NewMemLS(), | ||
}) | ||
|
||
return http.ListenAndServe(s.opts.ServePath, nil) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,25 @@ | ||
package inspector | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"math" | ||
"math/big" | ||
"net/http" | ||
"os" | ||
"path" | ||
"strings" | ||
"syscall" | ||
|
||
"archive/tar" | ||
"crypto/rand" | ||
|
||
docker "github.com/fsouza/go-dockerclient" | ||
"golang.org/x/net/webdav" | ||
|
||
iicmd "github.com/simon3z/image-inspector/pkg/cmd" | ||
apiserver "github.com/simon3z/image-inspector/pkg/imageserver" | ||
) | ||
|
||
type APIVersions struct { | ||
Versions []string `json:"versions"` | ||
} | ||
|
||
const ( | ||
VERSION_TAG = "v1" | ||
DOCKER_TAR_PREFIX = "rootfs/" | ||
|
@@ -47,11 +40,30 @@ type ImageInspector interface { | |
// defaultImageInspector is the default implementation of ImageInspector. | ||
type defaultImageInspector struct { | ||
opts iicmd.ImageInspectorOptions | ||
// an optional image server that will server content for inspection. | ||
imageServer apiserver.ImageServer | ||
} | ||
|
||
// NewDefaultImageInspector provides a new default inspector. | ||
func NewDefaultImageInspector(opts iicmd.ImageInspectorOptions) ImageInspector { | ||
return &defaultImageInspector{opts} | ||
inspector := &defaultImageInspector{ | ||
opts: opts, | ||
} | ||
|
||
// if serving then set up an image server | ||
if len(opts.Serve) > 0 { | ||
imageServerOpts := apiserver.ImageServerOptions{ | ||
ServePath: opts.Serve, | ||
HealthzURL: HEALTHZ_URL_PATH, | ||
APIURL: API_URL_PREFIX, | ||
APIVersions: apiserver.APIVersions{Versions: []string{VERSION_TAG}}, | ||
MetadataURL: METADATA_URL_PATH, | ||
ContentURL: CONTENT_URL_PREFIX, | ||
ImageServeURL: opts.DstPath, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually, this won't work here, need to set it later since we manipulate the |
||
} | ||
inspector.imageServer = apiserver.NewWebdavImageServer(imageServerOpts, opts.Chroot) | ||
} | ||
return inspector | ||
} | ||
|
||
// Inspect inspects and serves the image based on the ImageInspectorOptions. | ||
|
@@ -93,7 +105,7 @@ func (i *defaultImageInspector) Inspect() error { | |
container, err := client.CreateContainer(docker.CreateContainerOptions{ | ||
Name: randomName, | ||
Config: &docker.Config{ | ||
Image: i.opts.Image, | ||
Image: i.opts.Image, | ||
// For security purpose we don't define any entrypoint and command | ||
Entrypoint: []string{""}, | ||
Cmd: []string{""}, | ||
|
@@ -145,52 +157,8 @@ func (i *defaultImageInspector) Inspect() error { | |
ID: container.ID, | ||
}) | ||
|
||
supportedVersions := APIVersions{Versions: []string{VERSION_TAG}} | ||
|
||
if len(i.opts.Serve) > 0 { | ||
servePath := i.opts.DstPath | ||
if i.opts.Chroot { | ||
if err := syscall.Chroot(i.opts.DstPath); err != nil { | ||
return fmt.Errorf("Unable to chroot into %s: %v\n", i.opts.DstPath, err) | ||
} | ||
servePath = CHROOT_SERVE_PATH | ||
} else { | ||
log.Printf("!!!WARNING!!! It is insecure to serve the image content without changing") | ||
log.Printf("root (--chroot). Absolute-path symlinks in the image can lead to disclose") | ||
log.Printf("information of the hosting system.") | ||
} | ||
|
||
log.Printf("Serving image content %s on webdav://%s%s", i.opts.DstPath, i.opts.Serve, CONTENT_URL_PREFIX) | ||
|
||
http.HandleFunc(HEALTHZ_URL_PATH, func(w http.ResponseWriter, r *http.Request) { | ||
w.Write([]byte("ok\n")) | ||
}) | ||
|
||
http.HandleFunc(API_URL_PREFIX, func(w http.ResponseWriter, r *http.Request) { | ||
body, err := json.MarshalIndent(supportedVersions, "", " ") | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
w.Write(body) | ||
}) | ||
|
||
http.HandleFunc(METADATA_URL_PATH, func(w http.ResponseWriter, r *http.Request) { | ||
body, err := json.MarshalIndent(imageMetadata, "", " ") | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
w.Write(body) | ||
}) | ||
|
||
http.Handle(CONTENT_URL_PREFIX, &webdav.Handler{ | ||
Prefix: CONTENT_URL_PREFIX, | ||
FileSystem: webdav.Dir(servePath), | ||
LockSystem: webdav.NewMemLS(), | ||
}) | ||
|
||
return http.ListenAndServe(i.opts.Serve, nil) | ||
if i.imageServer != nil { | ||
return i.imageServer.ServeImage(imageMetadata) | ||
} | ||
return nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you call this file
webdav.go
and notimageserver.go
? not everything here is served with webdav.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll rename.