mirror of
https://github.com/genuinetools/reg.git
synced 2024-09-28 11:46:20 -04:00
242 lines
5.5 KiB
Go
242 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/distribution/manifest/schema1"
|
|
"github.com/jessfraz/reg/clair"
|
|
"github.com/jessfraz/reg/registry"
|
|
"github.com/jessfraz/reg/utils"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
const (
|
|
// VERSION is the binary version.
|
|
VERSION = "v0.2.0"
|
|
|
|
dockerConfigPath = ".docker/config.json"
|
|
)
|
|
|
|
var (
|
|
updating = false
|
|
wg sync.WaitGroup
|
|
r *registry.Registry
|
|
cl *clair.Clair
|
|
)
|
|
|
|
// preload initializes any global options and configuration
|
|
// before the main or sub commands are run.
|
|
func preload(c *cli.Context) (err error) {
|
|
if c.GlobalBool("debug") {
|
|
logrus.SetLevel(logrus.DebugLevel)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
app := cli.NewApp()
|
|
app.Name = "reg-server"
|
|
app.Version = VERSION
|
|
app.Author = "@jessfraz"
|
|
app.Email = "no-reply@butts.com"
|
|
app.Usage = "Docker registry v2 static UI server."
|
|
app.Before = preload
|
|
app.Flags = []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "debug, d",
|
|
Usage: "run in debug mode",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "username, u",
|
|
Usage: "username for the registry",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "password, p",
|
|
Usage: "password for the registry",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "registry, r",
|
|
Usage: "URL to the private registry (ex. r.j3ss.co)",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "insecure, k",
|
|
Usage: "do not verify tls certificates of registry",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "port",
|
|
Value: "8080",
|
|
Usage: "port for server to run on",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "cert",
|
|
Usage: "path to ssl cert",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "key",
|
|
Usage: "path to ssl key",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "interval",
|
|
Value: "5m",
|
|
Usage: "interval to generate new index.html's at",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "clair",
|
|
Usage: "url to clair instance",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "workers, w",
|
|
Value: 20,
|
|
Usage: "number of workers to analyse for vulnerabilities",
|
|
},
|
|
}
|
|
app.Action = func(c *cli.Context) error {
|
|
auth, err := utils.GetAuthConfig(c)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
// create the registry client
|
|
if c.GlobalBool("insecure") {
|
|
r, err = registry.NewInsecure(auth, c.GlobalBool("debug"))
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
} else {
|
|
r, err = registry.New(auth, c.GlobalBool("debug"))
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// parse the duration
|
|
dur, err := time.ParseDuration(c.String("interval"))
|
|
if err != nil {
|
|
logrus.Fatalf("parsing %s as duration failed: %v", c.String("interval"), err)
|
|
}
|
|
|
|
// create a clair instance if needed
|
|
if c.GlobalString("clair") != "" {
|
|
cl, err = clair.New(c.GlobalString("clair"), c.GlobalBool("debug"))
|
|
if err != nil {
|
|
logrus.Warnf("creation of clair failed: %v", err)
|
|
}
|
|
}
|
|
ticker := time.NewTicker(dur)
|
|
|
|
go func() {
|
|
// analyse repositories every X minutes based off interval
|
|
for range ticker.C {
|
|
if !updating {
|
|
logrus.Info("start repository analysis")
|
|
start := time.Now()
|
|
if err := analyseRepositories(r, cl, c.GlobalBool("debug"), c.GlobalInt("workers")); err != nil {
|
|
logrus.Warnf("repository analysis failed: %v", err)
|
|
wg.Wait()
|
|
updating = false
|
|
}
|
|
wg.Wait()
|
|
elapsed := time.Since(start)
|
|
logrus.Infof("finished repository analysis in %s", elapsed)
|
|
} else {
|
|
logrus.Warnf("skipping timer based repository analysis for %s", c.String("interval"))
|
|
}
|
|
}
|
|
}()
|
|
|
|
port := c.String("port")
|
|
keyfile := c.String("key")
|
|
certfile := c.String("cert")
|
|
|
|
logrus.Fatal(listenAndServe(port, keyfile, certfile, r, cl))
|
|
return nil
|
|
}
|
|
|
|
app.Run(os.Args)
|
|
}
|
|
|
|
type v1Compatibility struct {
|
|
ID string `json:"id"`
|
|
Created time.Time `json:"created"`
|
|
}
|
|
|
|
func analyseRepositories(r *registry.Registry, cl *clair.Clair, debug bool, workers int) error {
|
|
updating = true
|
|
logrus.Info("fetching catalog")
|
|
repoList, err := r.Catalog("")
|
|
if err != nil {
|
|
return fmt.Errorf("getting catalog failed: %v", err)
|
|
}
|
|
|
|
logrus.Info("fetching tags")
|
|
sem := make(chan int, workers)
|
|
for i, repo := range repoList {
|
|
// get the tags
|
|
tags, err := r.Tags(repo)
|
|
if err != nil {
|
|
return fmt.Errorf("getting tags for %s failed: %v", repo, err)
|
|
}
|
|
for j, tag := range tags {
|
|
// get the manifest
|
|
m1, err := r.ManifestV1(repo, tag)
|
|
if err != nil {
|
|
logrus.Warnf("getting v1 manifest for %s:%s failed: %v", repo, tag, err)
|
|
}
|
|
|
|
if cl != nil {
|
|
wg.Add(1)
|
|
sem <- 1
|
|
go func(repo, tag string, i, j int) {
|
|
defer func() {
|
|
wg.Done()
|
|
<-sem
|
|
}()
|
|
|
|
logrus.Infof("search vulnerabilities for %s:%s", repo, tag)
|
|
|
|
if err := searchVulnerabilities(r, cl, repo, tag, m1, debug); err != nil {
|
|
logrus.Warnf("searching vulnerabilities for %s:%s failed: %v", repo, tag, err)
|
|
}
|
|
}(repo, tag, i, j)
|
|
}
|
|
}
|
|
}
|
|
|
|
updating = false
|
|
return nil
|
|
}
|
|
|
|
func searchVulnerabilities(r *registry.Registry, cl *clair.Clair, repo, tag string, m schema1.SignedManifest, debug bool) error {
|
|
// filter out the empty layers
|
|
var filteredLayers []schema1.FSLayer
|
|
for _, layer := range m.FSLayers {
|
|
if layer.BlobSum != clair.EmptyLayerBlobSum {
|
|
filteredLayers = append(filteredLayers, layer)
|
|
}
|
|
}
|
|
m.FSLayers = filteredLayers
|
|
if len(m.FSLayers) == 0 {
|
|
fmt.Printf("No need to analyse image %s:%s as there is no non-emtpy layer", repo, tag)
|
|
return nil
|
|
}
|
|
|
|
for i := len(m.FSLayers) - 1; i >= 0; i-- {
|
|
// form the clair layer
|
|
l, err := cl.NewClairLayer(r, repo, m.FSLayers, i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// post the layer
|
|
if _, err := cl.PostLayer(l); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|