mirror of
https://github.com/genuinetools/reg.git
synced 2024-09-28 11:46:20 -04:00
cleanup and remove echo dependency
Signed-off-by: Jess Frazelle <acidburn@google.com>
This commit is contained in:
parent
9fdbf69688
commit
a6ba091b4a
6 changed files with 412 additions and 356 deletions
303
server/handlers.go
Normal file
303
server/handlers.go
Normal file
|
@ -0,0 +1,303 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/jessfraz/reg/clair"
|
||||
"github.com/jessfraz/reg/registry"
|
||||
)
|
||||
|
||||
type registryController struct {
|
||||
reg *registry.Registry
|
||||
cl *clair.Clair
|
||||
}
|
||||
|
||||
// A Repository holds data after a vulnerability scan of a single repo
|
||||
type Repository struct {
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag"`
|
||||
Created time.Time `json:"created"`
|
||||
URI string `json:"uri"`
|
||||
VulnerabilityReport clair.VulnerabilityReport `json:"vulnerability"`
|
||||
}
|
||||
|
||||
// A AnalysisResult holds all vulnerabilities of a scan
|
||||
type AnalysisResult struct {
|
||||
Repositories []Repository `json:"repositories"`
|
||||
RegistryDomain string `json:"registrydomain"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (rc *registryController) repositoriesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "repositories",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Debug("fetching repositories")
|
||||
|
||||
result := AnalysisResult{}
|
||||
result.RegistryDomain = rc.reg.Domain
|
||||
|
||||
repoList, err := rc.reg.Catalog("")
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "repositories",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("getting catalog failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
for _, repo := range repoList {
|
||||
repoURI := fmt.Sprintf("%s/%s", rc.reg.Domain, repo)
|
||||
r := Repository{
|
||||
Name: repo,
|
||||
URI: repoURI,
|
||||
}
|
||||
|
||||
result.Repositories = append(result.Repositories, r)
|
||||
}
|
||||
|
||||
if err := tmpl.ExecuteTemplate(w, "repositories", result); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "repositories",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("template rendering failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *registryController) tagHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "tag",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Debug("fetching tag")
|
||||
|
||||
vars := mux.Vars(r)
|
||||
repo := vars["repo"]
|
||||
tag := vars["tag"]
|
||||
|
||||
if repo == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty repo")
|
||||
return
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty tag")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "Repo: %s Tag: %s ", repo, tag)
|
||||
return
|
||||
}
|
||||
|
||||
func (rc *registryController) tagsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Debug("fetching tags")
|
||||
|
||||
vars := mux.Vars(r)
|
||||
repo := vars["repo"]
|
||||
if repo == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty repo")
|
||||
return
|
||||
}
|
||||
|
||||
tags, err := rc.reg.Tags(repo)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("getting tags for %s failed: %v", repo, err)
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "No tags found")
|
||||
return
|
||||
}
|
||||
|
||||
result := AnalysisResult{}
|
||||
result.RegistryDomain = rc.reg.Domain
|
||||
result.Name = repo
|
||||
for _, tag := range tags {
|
||||
// get the manifest
|
||||
m1, err := rc.reg.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("getting v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Manifest not found")
|
||||
return
|
||||
}
|
||||
|
||||
var createdDate time.Time
|
||||
for _, h := range m1.History {
|
||||
var comp v1Compatibility
|
||||
|
||||
if err := json.Unmarshal([]byte(h.V1Compatibility), &comp); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("unmarshal v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
createdDate = comp.Created
|
||||
break
|
||||
}
|
||||
|
||||
repoURI := fmt.Sprintf("%s/%s", rc.reg.Domain, repo)
|
||||
if tag != "latest" {
|
||||
repoURI += ":" + tag
|
||||
}
|
||||
rp := Repository{
|
||||
Name: repo,
|
||||
Tag: tag,
|
||||
URI: repoURI,
|
||||
Created: createdDate,
|
||||
}
|
||||
|
||||
if rc.cl != nil {
|
||||
vuln, err := rc.cl.Vulnerabilities(rc.reg, repo, tag, m1)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("vulnerability scanning for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rp.VulnerabilityReport = vuln
|
||||
}
|
||||
|
||||
result.Repositories = append(result.Repositories, rp)
|
||||
}
|
||||
|
||||
if err := tmpl.ExecuteTemplate(w, "tags", result); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("template rendering failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rc *registryController) vulnerabilitiesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Debug("fetching vulnerabilities")
|
||||
|
||||
vars := mux.Vars(r)
|
||||
repo := vars["repo"]
|
||||
tag := vars["tag"]
|
||||
|
||||
if repo == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty repo")
|
||||
return
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty tag")
|
||||
return
|
||||
}
|
||||
|
||||
m1, err := rc.reg.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("getting v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Manifest not found")
|
||||
return
|
||||
}
|
||||
|
||||
for _, h := range m1.History {
|
||||
var comp v1Compatibility
|
||||
|
||||
if err := json.Unmarshal([]byte(h.V1Compatibility), &comp); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("unmarshal v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
result := clair.VulnerabilityReport{}
|
||||
|
||||
if rc.cl != nil {
|
||||
result, err = rc.cl.Vulnerabilities(rc.reg, repo, tag, m1)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("vulnerability scanning for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if r.Header.Get("Accept-Encoding") == "application/json" {
|
||||
js, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("json marshal failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(js)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tmpl.ExecuteTemplate(w, "vulns", result); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("template rendering failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
110
server/server.go
110
server/server.go
|
@ -2,15 +2,21 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jessfraz/reg/clair"
|
||||
"github.com/jessfraz/reg/registry"
|
||||
"github.com/jessfraz/reg/utils"
|
||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -26,6 +32,7 @@ var (
|
|||
wg sync.WaitGroup
|
||||
r *registry.Registry
|
||||
cl *clair.Clair
|
||||
tmpl *template.Template
|
||||
)
|
||||
|
||||
// preload initializes any global options and configuration
|
||||
|
@ -114,12 +121,6 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
// 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"))
|
||||
|
@ -127,6 +128,19 @@ func main() {
|
|||
logrus.Warnf("creation of clair failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// create the initial index
|
||||
logrus.Info("creating initial static index")
|
||||
if err := analyseRepositories(r, cl, c.GlobalBool("debug"), c.GlobalInt("workers")); err != nil {
|
||||
logrus.Fatalf("Error creating index: %v", 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)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(dur)
|
||||
|
||||
go func() {
|
||||
|
@ -149,11 +163,87 @@ func main() {
|
|||
}
|
||||
}()
|
||||
|
||||
port := c.String("port")
|
||||
keyfile := c.String("key")
|
||||
certfile := c.String("cert")
|
||||
// get the path to the static directory
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
staticDir := filepath.Join(wd, "static")
|
||||
|
||||
// create the template
|
||||
templateDir := filepath.Join(staticDir, "../templates")
|
||||
|
||||
// make sure all the templates exist
|
||||
vulns := filepath.Join(templateDir, "vulns.html")
|
||||
if _, err := os.Stat(vulns); os.IsNotExist(err) {
|
||||
logrus.Fatalf("Template %s not found", vulns)
|
||||
}
|
||||
layout := filepath.Join(templateDir, "repositories.html")
|
||||
if _, err := os.Stat(layout); os.IsNotExist(err) {
|
||||
logrus.Fatalf("Template %s not found", layout)
|
||||
}
|
||||
tags := filepath.Join(templateDir, "tags.html")
|
||||
if _, err := os.Stat(tags); os.IsNotExist(err) {
|
||||
logrus.Fatalf("Template %s not found", tags)
|
||||
}
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"trim": func(s string) string {
|
||||
return wordwrap.WrapString(s, 80)
|
||||
},
|
||||
"color": func(s string) string {
|
||||
switch s = strings.ToLower(s); s {
|
||||
case "high":
|
||||
return "danger"
|
||||
case "critical":
|
||||
return "danger"
|
||||
case "defcon1":
|
||||
return "danger"
|
||||
case "medium":
|
||||
return "warning"
|
||||
case "low":
|
||||
return "info"
|
||||
case "negligible":
|
||||
return "info"
|
||||
case "unknown":
|
||||
return "default"
|
||||
default:
|
||||
return "default"
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
tmpl = template.Must(template.New("").Funcs(funcMap).ParseGlob(templateDir + "/*.html"))
|
||||
|
||||
rc := registryController{
|
||||
reg: r,
|
||||
cl: cl,
|
||||
}
|
||||
|
||||
// create mux server
|
||||
mux := mux.NewRouter()
|
||||
|
||||
// static files handler
|
||||
staticHandler := http.FileServer(http.Dir(staticDir))
|
||||
mux.HandleFunc("/", rc.tagsHandler)
|
||||
mux.Handle("/static", staticHandler)
|
||||
mux.HandleFunc("/repo/{repo}", rc.tagsHandler)
|
||||
mux.HandleFunc("/repo/{repo}/{tag}", rc.tagHandler)
|
||||
mux.HandleFunc("/repo/{repo}/{tag}/vulns", rc.vulnerabilitiesHandler)
|
||||
|
||||
// set up the server
|
||||
port := c.String("port")
|
||||
server := &http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: mux,
|
||||
}
|
||||
logrus.Infof("Starting server on port %q", port)
|
||||
if c.String("cert") != "" && c.String("key") != "" {
|
||||
logrus.Fatal(server.ListenAndServeTLS(c.String("cert"), c.String("key")))
|
||||
} else {
|
||||
logrus.Fatal(server.ListenAndServe())
|
||||
}
|
||||
|
||||
logrus.Fatal(listenAndServe(port, keyfile, certfile, r, cl))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
<base href="/" >
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>{{ .RegistryDomain }}</title>
|
||||
<link rel="icon" type="image/ico" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/styles.css" />
|
||||
<link rel="icon" type="image/ico" href="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ .RegistryDomain }}</h1>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<div class="footer">
|
||||
<a href="https://twitter.com/jessfraz">@jessfraz</a>
|
||||
</div><!--/.footer-->
|
||||
<script src="/js/scripts.js"></script>
|
||||
<script src="/static/js/scripts.js"></script>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
@ -9,8 +9,8 @@
|
|||
<base href="/" >
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>{{ .RegistryDomain }}/{{ .Name }}</title>
|
||||
<link rel="icon" type="image/ico" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/styles.css" />
|
||||
<link rel="icon" type="image/ico" href="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ .RegistryDomain }}/{{ .Name }}</h1>
|
||||
|
@ -50,7 +50,7 @@
|
|||
<div class="footer">
|
||||
<a href="https://twitter.com/jessfraz">@jessfraz</a>
|
||||
</div><!--/.footer-->
|
||||
<script src="/js/scripts.js"></script>
|
||||
<script src="/static/js/scripts.js"></script>
|
||||
<script type="text/javascript">
|
||||
var ajaxCalls = [
|
||||
{{ range $key, $value := .Repositories }}
|
|
@ -9,8 +9,8 @@
|
|||
<base href="/" >
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>{{ .RegistryURL }}/{{ .Repo }}:{{ .Tag }} Vulnerability Report</title>
|
||||
<link rel="icon" type="image/ico" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css" />
|
||||
<link rel="icon" type="image/ico" href="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
337
server/web.go
337
server/web.go
|
@ -1,337 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
||||
|
||||
"time"
|
||||
|
||||
"net/url"
|
||||
|
||||
"github.com/jessfraz/reg/clair"
|
||||
"github.com/jessfraz/reg/registry"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
type registryController struct {
|
||||
reg *registry.Registry
|
||||
cl *clair.Clair
|
||||
}
|
||||
|
||||
// A Template hold template data
|
||||
type Template struct {
|
||||
templates *template.Template
|
||||
}
|
||||
|
||||
// A Repository holds data after a vulnerability scan of a single repo
|
||||
type Repository struct {
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag"`
|
||||
Created time.Time `json:"created"`
|
||||
URI string `json:"uri"`
|
||||
VulnerabilityReport clair.VulnerabilityReport `json:"vulnerability"`
|
||||
}
|
||||
|
||||
// A AnalysisResult holds all vulnerabilities of a scan
|
||||
type AnalysisResult struct {
|
||||
Repositories []Repository `json:"repositories"`
|
||||
RegistryDomain string `json:"registrydomain"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Render a template
|
||||
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
return t.templates.ExecuteTemplate(w, name, data)
|
||||
}
|
||||
|
||||
func listenAndServe(port, keyfile, certfile string, r *registry.Registry, c *clair.Clair) error {
|
||||
e := echo.New()
|
||||
e.Use(middleware.Logger())
|
||||
e.Use(middleware.Recover())
|
||||
e.Use(middleware.Static("static"))
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"trim": func(s string) string {
|
||||
return wordwrap.WrapString(s, 80)
|
||||
},
|
||||
"color": func(s string) string {
|
||||
switch s = strings.ToLower(s); s {
|
||||
case "high":
|
||||
return "danger"
|
||||
case "critical":
|
||||
return "danger"
|
||||
case "defcon1":
|
||||
return "danger"
|
||||
case "medium":
|
||||
return "warning"
|
||||
case "low":
|
||||
return "info"
|
||||
case "negligible":
|
||||
return "info"
|
||||
case "unknown":
|
||||
return "default"
|
||||
default:
|
||||
return "default"
|
||||
}
|
||||
},
|
||||
}
|
||||
// precompile templates
|
||||
t := &Template{
|
||||
templates: template.Must(template.New("").Funcs(funcMap).ParseGlob("templates/echo/*.html")),
|
||||
}
|
||||
e.Renderer = t
|
||||
|
||||
rc := registryController{
|
||||
reg: r,
|
||||
cl: c,
|
||||
}
|
||||
|
||||
e.GET("/", func(c echo.Context) error {
|
||||
return c.Redirect(http.StatusMovedPermanently, "/repo")
|
||||
})
|
||||
|
||||
e.GET("/repo", rc.repositories)
|
||||
e.GET("/repo/:repo", rc.tags)
|
||||
e.GET("/repo/:repo/:tag", rc.tag)
|
||||
e.GET("/repo/:repo/:tag/vulns", rc.vulnerabilities)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: ":" + port,
|
||||
}
|
||||
|
||||
if keyfile != "" && certfile != "" {
|
||||
cer, err := tls.LoadX509KeyPair(certfile, keyfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.TLSConfig = &tls.Config{Certificates: []tls.Certificate{cer}}
|
||||
}
|
||||
|
||||
return e.StartServer(srv)
|
||||
}
|
||||
|
||||
func (rc *registryController) repositories(c echo.Context) error {
|
||||
log.WithFields(log.Fields{
|
||||
"method": "repositories",
|
||||
"context": c,
|
||||
}).Debug("fetching repositories")
|
||||
|
||||
result := AnalysisResult{}
|
||||
result.RegistryDomain = rc.reg.Domain
|
||||
|
||||
repoList, err := rc.reg.Catalog("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting catalog failed: %v", err)
|
||||
}
|
||||
for _, repo := range repoList {
|
||||
log.WithFields(log.Fields{
|
||||
"repo": repo,
|
||||
}).Debug("fetched repo")
|
||||
repoURI := fmt.Sprintf("%s/%s", rc.reg.Domain, repo)
|
||||
r := Repository{
|
||||
Name: repo,
|
||||
URI: repoURI,
|
||||
}
|
||||
|
||||
result.Repositories = append(result.Repositories, r)
|
||||
}
|
||||
err = c.Render(http.StatusOK, "repositories", result)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
}).Error("error during template rendering")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// e.GET("/repo/:repo/:tag", rc.tag)
|
||||
func (rc *registryController) tag(c echo.Context) error {
|
||||
repo, err := url.QueryUnescape(c.Param("repo"))
|
||||
if err != nil {
|
||||
return c.String(http.StatusNotFound, "Given repo can not be unescaped.")
|
||||
}
|
||||
if repo == "" {
|
||||
return c.String(http.StatusNotFound, "No repo given")
|
||||
}
|
||||
tag := c.Param("tag")
|
||||
if tag == "" {
|
||||
return c.String(http.StatusNotFound, "No tag given")
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, fmt.Sprintf("Repo: %s Tag: %s ", repo, tag))
|
||||
}
|
||||
|
||||
func (rc *registryController) tags(c echo.Context) error {
|
||||
repo, err := url.QueryUnescape(c.Param("repo"))
|
||||
if err != nil {
|
||||
return c.String(http.StatusNotFound, "Given repo can not be unescaped.")
|
||||
}
|
||||
if repo == "" {
|
||||
return c.String(http.StatusNotFound, "No repo given")
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"method": "tags",
|
||||
"context": c,
|
||||
"repo": repo,
|
||||
}).Info("fetching tags")
|
||||
|
||||
tags, err := rc.reg.Tags(repo)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"repo": repo,
|
||||
}).Error("getting tags failed.", repo, err)
|
||||
return c.String(http.StatusNotFound, "No Tags found")
|
||||
}
|
||||
|
||||
result := AnalysisResult{}
|
||||
result.RegistryDomain = rc.reg.Domain
|
||||
result.Name = repo
|
||||
for _, tag := range tags {
|
||||
// get the manifest
|
||||
|
||||
m1, err := r.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"repo": repo,
|
||||
"tag": tag,
|
||||
}).Warn("getting v1 manifest failed")
|
||||
}
|
||||
|
||||
var createdDate time.Time
|
||||
for _, h := range m1.History {
|
||||
var comp v1Compatibility
|
||||
if err := json.Unmarshal([]byte(h.V1Compatibility), &comp); err != nil {
|
||||
msg := "unmarshal v1compatibility failed"
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"repo": repo,
|
||||
"tag": tag,
|
||||
}).Warn(msg)
|
||||
return c.String(http.StatusInternalServerError, msg)
|
||||
}
|
||||
createdDate = comp.Created
|
||||
break
|
||||
}
|
||||
|
||||
repoURI := fmt.Sprintf("%s/%s", r.Domain, repo)
|
||||
if tag != "latest" {
|
||||
repoURI += ":" + tag
|
||||
}
|
||||
r := Repository{
|
||||
Name: repo,
|
||||
Tag: tag,
|
||||
URI: repoURI,
|
||||
Created: createdDate,
|
||||
}
|
||||
|
||||
if rc.cl != nil {
|
||||
vuln, err := rc.cl.Vulnerabilities(rc.reg, repo, tag, m1)
|
||||
if err != nil {
|
||||
msg := "error during vulnerability scanning."
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"repo": repo,
|
||||
"tag": tag,
|
||||
}).Error(msg)
|
||||
return c.String(http.StatusInternalServerError, msg)
|
||||
}
|
||||
r.VulnerabilityReport = vuln
|
||||
}
|
||||
|
||||
result.Repositories = append(result.Repositories, r)
|
||||
}
|
||||
err = c.Render(http.StatusOK, "tags", result)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
}).Error("error during template rendering")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (rc *registryController) vulnerabilities(c echo.Context) error {
|
||||
repo, err := url.QueryUnescape(c.Param("repo"))
|
||||
if err != nil {
|
||||
return c.String(http.StatusNotFound, "Given repo can not be unescaped.")
|
||||
}
|
||||
if repo == "" {
|
||||
return c.String(http.StatusNotFound, "No repo given")
|
||||
}
|
||||
tag := c.Param("tag")
|
||||
if tag == "" {
|
||||
return c.String(http.StatusNotFound, "No tag given")
|
||||
}
|
||||
|
||||
m1, err := r.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"repo": repo,
|
||||
"tag": tag,
|
||||
}).Warn("getting v1 manifest failed")
|
||||
}
|
||||
|
||||
for _, h := range m1.History {
|
||||
var comp v1Compatibility
|
||||
if err := json.Unmarshal([]byte(h.V1Compatibility), &comp); err != nil {
|
||||
msg := "unmarshal v1compatibility failed"
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"repo": repo,
|
||||
"tag": tag,
|
||||
}).Warn(msg)
|
||||
return c.String(http.StatusInternalServerError, msg)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
result := clair.VulnerabilityReport{}
|
||||
|
||||
if rc.cl != nil {
|
||||
result, err = rc.cl.Vulnerabilities(rc.reg, repo, tag, m1)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"repo": repo,
|
||||
"tag": tag,
|
||||
}).Error("error during vulnerability scanning.")
|
||||
}
|
||||
}
|
||||
|
||||
outputType := outputType(c.Request())
|
||||
if outputType == "json" {
|
||||
err = c.JSON(http.StatusOK, result)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
}).Error("error creating json response")
|
||||
}
|
||||
} else {
|
||||
err = c.Render(http.StatusOK, "vulns", result)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
}).Error("error during template rendering")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func outputType(r *http.Request) string {
|
||||
outputtype := "json"
|
||||
if r.Header.Get("Accept-Encoding") == "text/html" {
|
||||
outputtype = "html"
|
||||
}
|
||||
return outputtype
|
||||
}
|
Loading…
Reference in a new issue