Revert "update to use containerd seccomp package"
This reverts commit 4f8e065faf055d3f0463a92622297ca3afac07f4.
This commit is contained in:
parent
09243b740c
commit
60f032f6f5
8199 changed files with 1598219 additions and 30742 deletions
76
vendor/github.com/genuinetools/reg/clair/clair.go
generated
vendored
Normal file
76
vendor/github.com/genuinetools/reg/clair/clair.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Clair defines the client for retriving information from the clair API.
|
||||
type Clair struct {
|
||||
URL string
|
||||
Client *http.Client
|
||||
Logf LogfCallback
|
||||
}
|
||||
|
||||
// LogfCallback is the callback for formatting logs.
|
||||
type LogfCallback func(format string, args ...interface{})
|
||||
|
||||
// Quiet discards logs silently.
|
||||
func Quiet(format string, args ...interface{}) {}
|
||||
|
||||
// Log passes log messages to the logging package.
|
||||
func Log(format string, args ...interface{}) {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
|
||||
// New creates a new Clair struct with the given URL and credentials.
|
||||
func New(url string, debug bool) (*Clair, error) {
|
||||
transport := http.DefaultTransport
|
||||
|
||||
errorTransport := &ErrorTransport{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
// set the logging
|
||||
logf := Quiet
|
||||
if debug {
|
||||
logf = Log
|
||||
}
|
||||
|
||||
registry := &Clair{
|
||||
URL: url,
|
||||
Client: &http.Client{
|
||||
Timeout: 5 * time.Minute,
|
||||
Transport: errorTransport,
|
||||
},
|
||||
Logf: logf,
|
||||
}
|
||||
|
||||
return registry, nil
|
||||
}
|
||||
|
||||
// url returns a clair URL with the passed arguements concatenated.
|
||||
func (c *Clair) url(pathTemplate string, args ...interface{}) string {
|
||||
pathSuffix := fmt.Sprintf(pathTemplate, args...)
|
||||
url := fmt.Sprintf("%s%s", c.URL, pathSuffix)
|
||||
return url
|
||||
}
|
||||
|
||||
func (c *Clair) getJSON(url string, response interface{}) (http.Header, error) {
|
||||
resp, err := c.Client.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
c.Logf("clair.clair resp.Status=%s", resp.Status)
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
|
||||
c.Logf("clair.clair resp.Status=%s, body=%s", resp.Status, response)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Header, nil
|
||||
}
|
46
vendor/github.com/genuinetools/reg/clair/errortransport.go
generated
vendored
Normal file
46
vendor/github.com/genuinetools/reg/clair/errortransport.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type httpStatusError struct {
|
||||
Response *http.Response
|
||||
Body []byte // Copied from `Response.Body` to avoid problems with unclosed bodies later. Nobody calls `err.Response.Body.Close()`, ever.
|
||||
}
|
||||
|
||||
func (err *httpStatusError) Error() string {
|
||||
return fmt.Sprintf("http: non-successful response (status=%v body=%q)", err.Response.StatusCode, err.Body)
|
||||
}
|
||||
|
||||
var _ error = &httpStatusError{}
|
||||
|
||||
// ErrorTransport defines the data structure for returning errors from the round tripper.
|
||||
type ErrorTransport struct {
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
// RoundTrip defines the round tripper for the error transport.
|
||||
func (t *ErrorTransport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
resp, err := t.Transport.RoundTrip(request)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 500 {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("http: failed to read response body (status=%v, err=%q)", resp.StatusCode, err)
|
||||
}
|
||||
|
||||
return nil, &httpStatusError{
|
||||
Response: resp,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
78
vendor/github.com/genuinetools/reg/clair/layer.go
generated
vendored
Normal file
78
vendor/github.com/genuinetools/reg/clair/layer.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetLayer displays a Layer and optionally all of its features and vulnerabilities.
|
||||
func (c *Clair) GetLayer(name string, features, vulnerabilities bool) (*Layer, error) {
|
||||
url := c.url("/v1/layers/%s?features=%t&vulnerabilities=%t", name, features, vulnerabilities)
|
||||
c.Logf("clair.layers.get url=%s name=%s", url, name)
|
||||
|
||||
var respLayer layerEnvelope
|
||||
if _, err := c.getJSON(url, &respLayer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if respLayer.Error != nil {
|
||||
return nil, fmt.Errorf("clair error: %s", respLayer.Error.Message)
|
||||
}
|
||||
|
||||
return respLayer.Layer, nil
|
||||
}
|
||||
|
||||
// PostLayer performs the analysis of a Layer from the provided path.
|
||||
func (c *Clair) PostLayer(layer *Layer) (*Layer, error) {
|
||||
url := c.url("/v1/layers")
|
||||
c.Logf("clair.layers.post url=%s name=%s", url, layer.Name)
|
||||
|
||||
b, err := json.Marshal(layerEnvelope{Layer: layer})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.Client.Post(url, "application/json", bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
c.Logf("clair.clair resp.Status=%s", resp.Status)
|
||||
|
||||
var respLayer layerEnvelope
|
||||
if err := json.NewDecoder(resp.Body).Decode(&respLayer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if respLayer.Error != nil {
|
||||
return nil, fmt.Errorf("clair error: %s", respLayer.Error.Message)
|
||||
}
|
||||
|
||||
return respLayer.Layer, err
|
||||
}
|
||||
|
||||
// DeleteLayer removes a layer reference from clair.
|
||||
func (c *Clair) DeleteLayer(name string) error {
|
||||
url := c.url("/v1/layers/%s", name)
|
||||
c.Logf("clair.layers.delete url=%s name=%s", url, name)
|
||||
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.Client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
c.Logf("clair.clair resp.Status=%s", resp.Status)
|
||||
|
||||
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusAccepted || resp.StatusCode == http.StatusNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Got status code: %d", resp.StatusCode)
|
||||
}
|
76
vendor/github.com/genuinetools/reg/clair/types.go
generated
vendored
Normal file
76
vendor/github.com/genuinetools/reg/clair/types.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package clair
|
||||
|
||||
import "github.com/opencontainers/go-digest"
|
||||
|
||||
const (
|
||||
// EmptyLayerBlobSum is the blob sum of empty layers.
|
||||
EmptyLayerBlobSum = "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
|
||||
|
||||
// LegacyEmptyLayerBlobSum is the blob sum of empty layers used by docker
|
||||
// before it could support a truly empty layer.
|
||||
LegacyEmptyLayerBlobSum = "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
)
|
||||
|
||||
// IsEmptyLayer determines whether the blob sum is one of the known empty
|
||||
// layers.
|
||||
func IsEmptyLayer(blobSum digest.Digest) bool {
|
||||
return blobSum == EmptyLayerBlobSum || blobSum == LegacyEmptyLayerBlobSum
|
||||
}
|
||||
|
||||
var (
|
||||
// Priorities are the vulnerability priority labels.
|
||||
Priorities = []string{"Unknown", "Negligible", "Low", "Medium", "High", "Critical", "Defcon1", "Fixable"}
|
||||
)
|
||||
|
||||
// Error describes the structure of a clair error.
|
||||
type Error struct {
|
||||
Message string `json:"Message,omitempty"`
|
||||
}
|
||||
|
||||
// Layer represents an image layer.
|
||||
type Layer struct {
|
||||
Name string `json:"Name,omitempty"`
|
||||
NamespaceName string `json:"NamespaceName,omitempty"`
|
||||
Path string `json:"Path,omitempty"`
|
||||
Headers map[string]string `json:"Headers,omitempty"`
|
||||
ParentName string `json:"ParentName,omitempty"`
|
||||
Format string `json:"Format,omitempty"`
|
||||
IndexedByVersion int `json:"IndexedByVersion,omitempty"`
|
||||
Features []feature `json:"Features,omitempty"`
|
||||
}
|
||||
|
||||
type layerEnvelope struct {
|
||||
Layer *Layer `json:"Layer,omitempty"`
|
||||
Error *Error `json:"Error,omitempty"`
|
||||
}
|
||||
|
||||
// Vulnerability represents vulnerability entity returned by Clair.
|
||||
type Vulnerability struct {
|
||||
Name string `json:"Name,omitempty"`
|
||||
NamespaceName string `json:"NamespaceName,omitempty"`
|
||||
Description string `json:"Description,omitempty"`
|
||||
Link string `json:"Link,omitempty"`
|
||||
Severity string `json:"Severity,omitempty"`
|
||||
Metadata map[string]interface{} `json:"Metadata,omitempty"`
|
||||
FixedBy string `json:"FixedBy,omitempty"`
|
||||
FixedIn []feature `json:"FixedIn,omitempty"`
|
||||
}
|
||||
|
||||
// VulnerabilityReport represents the result of a vulnerability scan of a repo.
|
||||
type VulnerabilityReport struct {
|
||||
RegistryURL string
|
||||
Repo string
|
||||
Tag string
|
||||
Date string
|
||||
Vulns []Vulnerability
|
||||
VulnsBySeverity map[string][]Vulnerability
|
||||
BadVulns int
|
||||
}
|
||||
type feature struct {
|
||||
Name string `json:"Name,omitempty"`
|
||||
NamespaceName string `json:"NamespaceName,omitempty"`
|
||||
VersionFormat string `json:"VersionFormat,omitempty"`
|
||||
Version string `json:"Version,omitempty"`
|
||||
Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"`
|
||||
AddedBy string `json:"AddedBy,omitempty"`
|
||||
}
|
131
vendor/github.com/genuinetools/reg/clair/vulns.go
generated
vendored
Normal file
131
vendor/github.com/genuinetools/reg/clair/vulns.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
)
|
||||
|
||||
// Vulnerabilities scans the given repo and tag
|
||||
func (c *Clair) Vulnerabilities(r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
|
||||
report := VulnerabilityReport{
|
||||
RegistryURL: r.Domain,
|
||||
Repo: repo,
|
||||
Tag: tag,
|
||||
Date: time.Now().Local().Format(time.RFC1123),
|
||||
VulnsBySeverity: make(map[string][]Vulnerability),
|
||||
}
|
||||
|
||||
// Get the v1 manifest to pass to clair.
|
||||
m, err := r.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
return report, fmt.Errorf("getting the v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
}
|
||||
|
||||
// Filter out the empty layers.
|
||||
var filteredLayers []schema1.FSLayer
|
||||
for _, layer := range m.FSLayers {
|
||||
if layer.BlobSum != 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 report, nil
|
||||
}
|
||||
|
||||
for i := len(m.FSLayers) - 1; i >= 0; i-- {
|
||||
// Form the clair layer.
|
||||
l, err := c.NewClairLayer(r, repo, m.FSLayers, i)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
// Post the layer.
|
||||
if _, err := c.PostLayer(l); err != nil {
|
||||
return report, err
|
||||
}
|
||||
}
|
||||
|
||||
vl, err := c.GetLayer(m.FSLayers[0].BlobSum.String(), false, true)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
// Get the vulns.
|
||||
for _, f := range vl.Features {
|
||||
report.Vulns = append(report.Vulns, f.Vulnerabilities...)
|
||||
}
|
||||
|
||||
vulnsBy := func(sev string, store map[string][]Vulnerability) []Vulnerability {
|
||||
items, found := store[sev]
|
||||
if !found {
|
||||
items = make([]Vulnerability, 0)
|
||||
store[sev] = items
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// group by severity
|
||||
for _, v := range report.Vulns {
|
||||
sevRow := vulnsBy(v.Severity, report.VulnsBySeverity)
|
||||
report.VulnsBySeverity[v.Severity] = append(sevRow, v)
|
||||
}
|
||||
|
||||
// calculate number of bad vulns
|
||||
report.BadVulns = len(report.VulnsBySeverity["High"]) + len(report.VulnsBySeverity["Critical"]) + len(report.VulnsBySeverity["Defcon1"])
|
||||
|
||||
return report, nil
|
||||
}
|
||||
|
||||
// NewClairLayer will form a layer struct required for a clar scan
|
||||
func (c *Clair) NewClairLayer(r *registry.Registry, image string, fsLayers []schema1.FSLayer, index int) (*Layer, error) {
|
||||
var parentName string
|
||||
if index < len(fsLayers)-1 {
|
||||
parentName = fsLayers[index+1].BlobSum.String()
|
||||
}
|
||||
|
||||
// form the path
|
||||
p := strings.Join([]string{r.URL, "v2", image, "blobs", fsLayers[index].BlobSum.String()}, "/")
|
||||
|
||||
useBasicAuth := false
|
||||
|
||||
// get the token
|
||||
token, err := r.Token(p)
|
||||
if err != nil {
|
||||
// if we get an error here of type: malformed auth challenge header: 'Basic realm="Registry Realm"'
|
||||
// we need to use basic auth for the registry
|
||||
if !strings.Contains(err.Error(), `malformed auth challenge header: 'Basic realm="Registry`) {
|
||||
return nil, err
|
||||
}
|
||||
useBasicAuth = true
|
||||
}
|
||||
|
||||
h := make(map[string]string)
|
||||
if token != "" && !useBasicAuth {
|
||||
h = map[string]string{
|
||||
"Authorization": fmt.Sprintf("Bearer %s", token),
|
||||
}
|
||||
}
|
||||
|
||||
if token == "" || useBasicAuth {
|
||||
c.Logf("clair.vulns using basic auth")
|
||||
h = map[string]string{
|
||||
"Authorization": fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(r.Username+":"+r.Password))),
|
||||
}
|
||||
}
|
||||
|
||||
return &Layer{
|
||||
Name: fsLayers[index].BlobSum.String(),
|
||||
Path: p,
|
||||
ParentName: parentName,
|
||||
Format: "Docker",
|
||||
Headers: h,
|
||||
}, nil
|
||||
}
|
34
vendor/github.com/genuinetools/reg/server/Dockerfile
generated
vendored
Normal file
34
vendor/github.com/genuinetools/reg/server/Dockerfile
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
FROM golang:alpine as builder
|
||||
MAINTAINER Jessica Frazelle <jess@linux.com>
|
||||
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates
|
||||
|
||||
RUN set -x \
|
||||
&& apk add --no-cache --virtual .build-deps \
|
||||
git \
|
||||
gcc \
|
||||
libc-dev \
|
||||
libgcc \
|
||||
&& go get -v github.com/genuinetools/reg \
|
||||
&& cd /go/src/github.com/genuinetools/reg \
|
||||
&& CGO_ENABLED=0 go build -a -tags netgo -ldflags '-extldflags "-static"' -o /usr/bin/reg-server ./server \
|
||||
&& apk del .build-deps \
|
||||
&& rm -rf /go \
|
||||
&& echo "Build complete."
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /usr/bin/reg-server /usr/bin/reg-server
|
||||
COPY --from=builder /etc/ssl/certs/ /etc/ssl/certs
|
||||
|
||||
COPY static /src/static
|
||||
COPY templates /src/templates
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
ENTRYPOINT [ "reg-server" ]
|
||||
CMD [ "--help" ]
|
46
vendor/github.com/genuinetools/reg/server/README.md
generated
vendored
Normal file
46
vendor/github.com/genuinetools/reg/server/README.md
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
# reg-server
|
||||
|
||||
A static UI for a docker registry. Comes with vulnerability scanning if you
|
||||
have a [CoreOS Clair](https://github.com/coreos/clair) server set up.
|
||||
|
||||
Demo at [r.j3ss.co](https://r.j3ss.co).
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
$ reg-server -h
|
||||
NAME:
|
||||
reg-server - Docker registry v2 static UI server.
|
||||
|
||||
USAGE:
|
||||
reg-server [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
v0.1.0
|
||||
|
||||
AUTHOR:
|
||||
The Genuinetools Authors <no-reply@butts.com>
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--debug, -d run in debug mode
|
||||
--username value, -u value username for the registry
|
||||
--password value, -p value password for the registry
|
||||
--registry value, -r value URL to the private registry (ex. r.j3ss.co)
|
||||
--insecure, -k do not verify tls certificates of registry
|
||||
--port value port for server to run on (default: "8080")
|
||||
--cert value path to ssl cert
|
||||
--key value path to ssl key
|
||||
--interval value interval to generate new index.html's at (default: "1h")
|
||||
--clair value url to clair instance
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||

|
257
vendor/github.com/genuinetools/reg/server/handlers.go
generated
vendored
Normal file
257
vendor/github.com/genuinetools/reg/server/handlers.go
generated
vendored
Normal file
|
@ -0,0 +1,257 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/genuinetools/reg/clair"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type registryController struct {
|
||||
reg *registry.Registry
|
||||
cl *clair.Clair
|
||||
}
|
||||
|
||||
type v1Compatibility struct {
|
||||
ID string `json:"id"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
LastUpdated string `json:"lastUpdated"`
|
||||
}
|
||||
|
||||
func (rc *registryController) repositories(staticDir string) error {
|
||||
updating = true
|
||||
logrus.Info("fetching catalog")
|
||||
|
||||
result := AnalysisResult{
|
||||
RegistryDomain: rc.reg.Domain,
|
||||
LastUpdated: time.Now().Local().Format(time.RFC1123),
|
||||
}
|
||||
|
||||
repoList, err := r.Catalog("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting catalog failed: %v", err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// parse & execute the template
|
||||
logrus.Info("executing the template repositories")
|
||||
|
||||
path := filepath.Join(staticDir, "index.html")
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("creating/opening file %s", path)
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := tmpl.ExecuteTemplate(f, "repositories", result); err != nil {
|
||||
f.Close()
|
||||
return fmt.Errorf("execute template repositories failed: %v", err)
|
||||
}
|
||||
|
||||
updating = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *registryController) tagsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Info("fetching tags")
|
||||
|
||||
vars := mux.Vars(r)
|
||||
repo, err := url.QueryUnescape(vars["repo"])
|
||||
if err != nil || repo == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty repo")
|
||||
return
|
||||
}
|
||||
|
||||
tags, err := rc.reg.Tags(repo)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.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
|
||||
}
|
||||
|
||||
// Error out if there are no tags / images (the above err != nil does not error out when nothing has been found)
|
||||
if len(tags) == 0 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "No tags found")
|
||||
return
|
||||
}
|
||||
|
||||
result := AnalysisResult{
|
||||
RegistryDomain: rc.reg.Domain,
|
||||
LastUpdated: time.Now().Local().Format(time.RFC1123),
|
||||
Name: repo,
|
||||
}
|
||||
|
||||
for _, tag := range tags {
|
||||
// get the manifest
|
||||
m1, err := rc.reg.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
"repo": repo,
|
||||
"tag": tag,
|
||||
}).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 {
|
||||
logrus.WithFields(logrus.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,
|
||||
}
|
||||
|
||||
result.Repositories = append(result.Repositories, rp)
|
||||
}
|
||||
|
||||
if err := tmpl.ExecuteTemplate(w, "tags", result); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("template rendering failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *registryController) vulnerabilitiesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Info("fetching vulnerabilities")
|
||||
|
||||
vars := mux.Vars(r)
|
||||
repo, err := url.QueryUnescape(vars["repo"])
|
||||
tag := vars["tag"]
|
||||
|
||||
if err != nil || repo == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty repo")
|
||||
return
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty tag")
|
||||
return
|
||||
}
|
||||
|
||||
result := clair.VulnerabilityReport{}
|
||||
|
||||
if rc.cl != nil {
|
||||
result, err = rc.cl.Vulnerabilities(rc.reg, repo, tag)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.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 strings.HasSuffix(r.URL.String(), ".json") {
|
||||
js, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.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 {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("template rendering failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
BIN
vendor/github.com/genuinetools/reg/server/home.png
generated
vendored
Normal file
BIN
vendor/github.com/genuinetools/reg/server/home.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
248
vendor/github.com/genuinetools/reg/server/server.go
generated
vendored
Normal file
248
vendor/github.com/genuinetools/reg/server/server.go
generated
vendored
Normal file
|
@ -0,0 +1,248 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/genuinetools/reg/clair"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
"github.com/gorilla/mux"
|
||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
// VERSION is the binary version.
|
||||
VERSION = "v0.2.0"
|
||||
)
|
||||
|
||||
var (
|
||||
updating = false
|
||||
r *registry.Registry
|
||||
cl *clair.Clair
|
||||
tmpl *template.Template
|
||||
)
|
||||
|
||||
// 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 = "The Genuinetools Authors"
|
||||
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.BoolFlag{
|
||||
Name: "once, o",
|
||||
Usage: "generate an output once and then exit",
|
||||
},
|
||||
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: "1h",
|
||||
Usage: "interval to generate new index.html's at",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "clair",
|
||||
Usage: "url to clair instance",
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) error {
|
||||
auth, err := repoutils.GetAuthConfig(c.GlobalString("username"), c.GlobalString("password"), c.GlobalString("registry"))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 the initial index
|
||||
logrus.Info("creating initial static index")
|
||||
if err := rc.repositories(staticDir); err != nil {
|
||||
logrus.Fatalf("Error creating index: %v", err)
|
||||
}
|
||||
|
||||
if c.GlobalBool("once") {
|
||||
logrus.Info("Output generated")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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() {
|
||||
// create more indexes every X minutes based off interval
|
||||
for range ticker.C {
|
||||
if !updating {
|
||||
logrus.Info("creating timer based static index")
|
||||
if err := rc.repositories(staticDir); err != nil {
|
||||
logrus.Warnf("creating static index failed: %v", err)
|
||||
updating = false
|
||||
}
|
||||
} else {
|
||||
logrus.Warnf("skipping timer based static index update for %s", c.String("interval"))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// create mux server
|
||||
mux := mux.NewRouter()
|
||||
mux.UseEncodedPath()
|
||||
|
||||
// static files handler
|
||||
staticHandler := http.FileServer(http.Dir(staticDir))
|
||||
mux.HandleFunc("/repo/{repo}/tags", rc.tagsHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tags/", rc.tagsHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}", rc.vulnerabilitiesHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}/", rc.vulnerabilitiesHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns", rc.vulnerabilitiesHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns/", rc.vulnerabilitiesHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns.json", rc.vulnerabilitiesHandler)
|
||||
mux.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticHandler))
|
||||
mux.Handle("/", staticHandler)
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
596
vendor/github.com/genuinetools/reg/server/static/css/bootstrap-theme.css
generated
vendored
Normal file
596
vendor/github.com/genuinetools/reg/server/static/css/bootstrap-theme.css
generated
vendored
Normal file
|
@ -0,0 +1,596 @@
|
|||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2017 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Generated using the Bootstrap Customizer (https://getbootstrap.com/customize/?id=5343e9cbe6bb765ae40092b46ea944d5)
|
||||
* Config saved to config.json and https://gist.github.com/5343e9cbe6bb765ae40092b46ea944d5
|
||||
*/
|
||||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
.btn-default,
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
.btn-info,
|
||||
.btn-warning,
|
||||
.btn-danger {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-primary:active,
|
||||
.btn-success:active,
|
||||
.btn-info:active,
|
||||
.btn-warning:active,
|
||||
.btn-danger:active,
|
||||
.btn-default.active,
|
||||
.btn-primary.active,
|
||||
.btn-success.active,
|
||||
.btn-info.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active {
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-primary.disabled,
|
||||
.btn-success.disabled,
|
||||
.btn-info.disabled,
|
||||
.btn-warning.disabled,
|
||||
.btn-danger.disabled,
|
||||
.btn-default[disabled],
|
||||
.btn-primary[disabled],
|
||||
.btn-success[disabled],
|
||||
.btn-info[disabled],
|
||||
.btn-warning[disabled],
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
fieldset[disabled] .btn-primary,
|
||||
fieldset[disabled] .btn-success,
|
||||
fieldset[disabled] .btn-info,
|
||||
fieldset[disabled] .btn-warning,
|
||||
fieldset[disabled] .btn-danger {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-default .badge,
|
||||
.btn-primary .badge,
|
||||
.btn-success .badge,
|
||||
.btn-info .badge,
|
||||
.btn-warning .badge,
|
||||
.btn-danger .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
background-image: none;
|
||||
}
|
||||
.btn-default {
|
||||
background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
|
||||
background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#e0e0e0));
|
||||
background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dbdbdb;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
border-color: #ccc;
|
||||
}
|
||||
.btn-default:hover,
|
||||
.btn-default:focus {
|
||||
background-color: #e0e0e0;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
border-color: #dbdbdb;
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-default[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
.btn-default.disabled:hover,
|
||||
.btn-default[disabled]:hover,
|
||||
fieldset[disabled] .btn-default:hover,
|
||||
.btn-default.disabled:focus,
|
||||
.btn-default[disabled]:focus,
|
||||
fieldset[disabled] .btn-default:focus,
|
||||
.btn-default.disabled.focus,
|
||||
.btn-default[disabled].focus,
|
||||
fieldset[disabled] .btn-default.focus,
|
||||
.btn-default.disabled:active,
|
||||
.btn-default[disabled]:active,
|
||||
fieldset[disabled] .btn-default:active,
|
||||
.btn-default.disabled.active,
|
||||
.btn-default[disabled].active,
|
||||
fieldset[disabled] .btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: #265a88;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-primary:active,
|
||||
.btn-primary.active {
|
||||
background-color: #265a88;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary.disabled,
|
||||
.btn-primary[disabled],
|
||||
fieldset[disabled] .btn-primary,
|
||||
.btn-primary.disabled:hover,
|
||||
.btn-primary[disabled]:hover,
|
||||
fieldset[disabled] .btn-primary:hover,
|
||||
.btn-primary.disabled:focus,
|
||||
.btn-primary[disabled]:focus,
|
||||
fieldset[disabled] .btn-primary:focus,
|
||||
.btn-primary.disabled.focus,
|
||||
.btn-primary[disabled].focus,
|
||||
fieldset[disabled] .btn-primary.focus,
|
||||
.btn-primary.disabled:active,
|
||||
.btn-primary[disabled]:active,
|
||||
fieldset[disabled] .btn-primary:active,
|
||||
.btn-primary.disabled.active,
|
||||
.btn-primary[disabled].active,
|
||||
fieldset[disabled] .btn-primary.active {
|
||||
background-color: #265a88;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success:hover,
|
||||
.btn-success:focus {
|
||||
background-color: #419641;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-success:active,
|
||||
.btn-success.active {
|
||||
background-color: #419641;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success.disabled,
|
||||
.btn-success[disabled],
|
||||
fieldset[disabled] .btn-success,
|
||||
.btn-success.disabled:hover,
|
||||
.btn-success[disabled]:hover,
|
||||
fieldset[disabled] .btn-success:hover,
|
||||
.btn-success.disabled:focus,
|
||||
.btn-success[disabled]:focus,
|
||||
fieldset[disabled] .btn-success:focus,
|
||||
.btn-success.disabled.focus,
|
||||
.btn-success[disabled].focus,
|
||||
fieldset[disabled] .btn-success.focus,
|
||||
.btn-success.disabled:active,
|
||||
.btn-success[disabled]:active,
|
||||
fieldset[disabled] .btn-success:active,
|
||||
.btn-success.disabled.active,
|
||||
.btn-success[disabled].active,
|
||||
fieldset[disabled] .btn-success.active {
|
||||
background-color: #419641;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info:hover,
|
||||
.btn-info:focus {
|
||||
background-color: #2aabd2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-info:active,
|
||||
.btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info.disabled,
|
||||
.btn-info[disabled],
|
||||
fieldset[disabled] .btn-info,
|
||||
.btn-info.disabled:hover,
|
||||
.btn-info[disabled]:hover,
|
||||
fieldset[disabled] .btn-info:hover,
|
||||
.btn-info.disabled:focus,
|
||||
.btn-info[disabled]:focus,
|
||||
fieldset[disabled] .btn-info:focus,
|
||||
.btn-info.disabled.focus,
|
||||
.btn-info[disabled].focus,
|
||||
fieldset[disabled] .btn-info.focus,
|
||||
.btn-info.disabled:active,
|
||||
.btn-info[disabled]:active,
|
||||
fieldset[disabled] .btn-info:active,
|
||||
.btn-info.disabled.active,
|
||||
.btn-info[disabled].active,
|
||||
fieldset[disabled] .btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning:hover,
|
||||
.btn-warning:focus {
|
||||
background-color: #eb9316;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-warning:active,
|
||||
.btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning.disabled,
|
||||
.btn-warning[disabled],
|
||||
fieldset[disabled] .btn-warning,
|
||||
.btn-warning.disabled:hover,
|
||||
.btn-warning[disabled]:hover,
|
||||
fieldset[disabled] .btn-warning:hover,
|
||||
.btn-warning.disabled:focus,
|
||||
.btn-warning[disabled]:focus,
|
||||
fieldset[disabled] .btn-warning:focus,
|
||||
.btn-warning.disabled.focus,
|
||||
.btn-warning[disabled].focus,
|
||||
fieldset[disabled] .btn-warning.focus,
|
||||
.btn-warning.disabled:active,
|
||||
.btn-warning[disabled]:active,
|
||||
fieldset[disabled] .btn-warning:active,
|
||||
.btn-warning.disabled.active,
|
||||
.btn-warning[disabled].active,
|
||||
fieldset[disabled] .btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus {
|
||||
background-color: #c12e2a;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-danger:active,
|
||||
.btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger.disabled,
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-danger,
|
||||
.btn-danger.disabled:hover,
|
||||
.btn-danger[disabled]:hover,
|
||||
fieldset[disabled] .btn-danger:hover,
|
||||
.btn-danger.disabled:focus,
|
||||
.btn-danger[disabled]:focus,
|
||||
fieldset[disabled] .btn-danger:focus,
|
||||
.btn-danger.disabled.focus,
|
||||
.btn-danger[disabled].focus,
|
||||
fieldset[disabled] .btn-danger.focus,
|
||||
.btn-danger.disabled:active,
|
||||
.btn-danger[disabled]:active,
|
||||
fieldset[disabled] .btn-danger:active,
|
||||
.btn-danger.disabled.active,
|
||||
.btn-danger[disabled].active,
|
||||
fieldset[disabled] .btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
background-image: none;
|
||||
}
|
||||
.thumbnail,
|
||||
.img-thumbnail {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-color: #2e6da4;
|
||||
}
|
||||
.navbar-default {
|
||||
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
|
||||
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
|
||||
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .open > a,
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
|
||||
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.navbar-brand,
|
||||
.navbar-nav > li > a {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);
|
||||
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222222));
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .open > a,
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
|
||||
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.navbar-inverse .navbar-brand,
|
||||
.navbar-inverse .navbar-nav > li > a {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.navbar-static-top,
|
||||
.navbar-fixed-top,
|
||||
.navbar-fixed-bottom {
|
||||
border-radius: 0;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||
color: #fff;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
}
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.alert-success {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||
border-color: #b2dba1;
|
||||
}
|
||||
.alert-info {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||
border-color: #9acfea;
|
||||
}
|
||||
.alert-warning {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||
border-color: #f5e79e;
|
||||
}
|
||||
.alert-danger {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||
border-color: #dca7a7;
|
||||
}
|
||||
.progress {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
|
||||
}
|
||||
.progress-bar-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||
}
|
||||
.progress-bar-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||
}
|
||||
.progress-bar-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||
}
|
||||
.progress-bar-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||
}
|
||||
.progress-bar-striped {
|
||||
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||
}
|
||||
.list-group {
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #286090;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.list-group-item.active .badge,
|
||||
.list-group-item.active:hover .badge,
|
||||
.list-group-item.active:focus .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.panel-default > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||
}
|
||||
.panel-info > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||
}
|
||||
.panel-warning > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||
}
|
||||
.panel-danger > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||
}
|
||||
.well {
|
||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||
border-color: #dcdcdc;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
14
vendor/github.com/genuinetools/reg/server/static/css/bootstrap-theme.min.css
generated
vendored
Normal file
14
vendor/github.com/genuinetools/reg/server/static/css/bootstrap-theme.min.css
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
3853
vendor/github.com/genuinetools/reg/server/static/css/bootstrap.css
generated
vendored
Normal file
3853
vendor/github.com/genuinetools/reg/server/static/css/bootstrap.css
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
14
vendor/github.com/genuinetools/reg/server/static/css/bootstrap.min.css
generated
vendored
Normal file
14
vendor/github.com/genuinetools/reg/server/static/css/bootstrap.min.css
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
16
vendor/github.com/genuinetools/reg/server/static/css/search.svg
generated
vendored
Normal file
16
vendor/github.com/genuinetools/reg/server/static/css/search.svg
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
|
||||
<g id="search_1_">
|
||||
<path fill="#CCCCCC" d="M20,0.005c-6.627,0-12,5.373-12,12c0,2.026,0.507,3.933,1.395,5.608l-8.344,8.342l0.007,0.006
|
||||
C0.406,26.602,0,27.49,0,28.477c0,1.949,1.58,3.529,3.529,3.529c0.985,0,1.874-0.406,2.515-1.059l-0.002-0.002l8.341-8.34
|
||||
c1.676,0.891,3.586,1.4,5.617,1.4c6.627,0,12-5.373,12-12S26.627,0.005,20,0.005z M4.795,29.697
|
||||
c-0.322,0.334-0.768,0.543-1.266,0.543c-0.975,0-1.765-0.789-1.765-1.764c0-0.498,0.21-0.943,0.543-1.266l-0.009-0.008l8.066-8.066
|
||||
c0.705,0.951,1.545,1.791,2.494,2.498L4.795,29.697z M20,22.006c-5.522,0-10-4.479-10-10c0-5.522,4.478-10,10-10
|
||||
c5.521,0,10,4.478,10,10C30,17.527,25.521,22.006,20,22.006z"/>
|
||||
<path fill="#CCCCCC" d="M20,5.005c-3.867,0-7,3.134-7,7c0,0.276,0.224,0.5,0.5,0.5s0.5-0.224,0.5-0.5c0-3.313,2.686-6,6-6
|
||||
c0.275,0,0.5-0.224,0.5-0.5S20.275,5.005,20,5.005z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
252
vendor/github.com/genuinetools/reg/server/static/css/styles.css
generated
vendored
Normal file
252
vendor/github.com/genuinetools/reg/server/static/css/styles.css
generated
vendored
Normal file
|
@ -0,0 +1,252 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,300');
|
||||
/* Have to use @import for the font, as you can only specify a single stylesheet */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html {
|
||||
min-height: 100%;
|
||||
border-top: 10px solid #ECEEF1;
|
||||
border-bottom: 10px solid #ECEEF1;
|
||||
color: #61666c;
|
||||
font-weight: 300;
|
||||
font-size: 1em;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
line-height: 2em;
|
||||
}
|
||||
body {
|
||||
padding: 20px;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
code {
|
||||
font-family: Inconsolata,monospace;
|
||||
}
|
||||
a {
|
||||
color: #61666c;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #2a2a2a;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Wrapper
|
||||
\*------------------------------------*/
|
||||
.wrapper {
|
||||
margin: 0 auto;
|
||||
padding-top: 20px;
|
||||
max-width: 800px;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Demo block
|
||||
\*------------------------------------*/
|
||||
.block {
|
||||
font-size: .875em;
|
||||
margin: 20px 0;
|
||||
padding: 20px;
|
||||
color: #9099A3;
|
||||
}
|
||||
h1 {
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
font-size: 1.4em;
|
||||
line-height: 3em;
|
||||
font-family: 'Museo Slab', 'Open Sans', monospace;
|
||||
}
|
||||
form {
|
||||
text-align: center;
|
||||
}
|
||||
input {
|
||||
margin: 0 auto;
|
||||
font-size: 100%;
|
||||
vertical-align: middle;
|
||||
*overflow: visible;
|
||||
line-height: normal;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
padding: 4px 32px 4px 6px;
|
||||
margin-bottom: 9px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #555555;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
width: 196px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
background: url('search.svg') no-repeat 211px center;
|
||||
background-size: auto 20px;
|
||||
}
|
||||
input:focus {
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
outline: 0;
|
||||
outline: thin dotted \9;
|
||||
/* IE6-9 */
|
||||
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
}
|
||||
input::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
input[type="search"] {
|
||||
margin-top: 20px;
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
-webkit-appearance: textfield;
|
||||
-webkit-transition: all 300ms ease-in;
|
||||
-moz-transition: all 300ms ease-in;
|
||||
-ms-transition: all 300ms ease-in;
|
||||
-o-transition: all 300ms ease-in;
|
||||
transition: all 300ms ease-in;
|
||||
}
|
||||
input[type="search"]::-webkit-search-decoration,
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
a.clear,
|
||||
a.clear:link,
|
||||
a.clear:visited {
|
||||
color: #666;
|
||||
padding: 2px 0 2px 0;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
margin: 0px 0 0 20px;
|
||||
line-height: 14px;
|
||||
display: inline-block;
|
||||
border-bottom: transparent 1px solid;
|
||||
vertical-align: -10px;
|
||||
-webkit-transition: all 300ms ease-in;
|
||||
-moz-transition: all 300ms ease-in;
|
||||
-ms-transition: all 300ms ease-in;
|
||||
-o-transition: all 300ms ease-in;
|
||||
transition: all 300ms ease-in;
|
||||
}
|
||||
a.clear:hover {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Table (directory listing)
|
||||
\*------------------------------------*/
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
font-size: .875em;
|
||||
max-width: 100%;
|
||||
margin: 20px auto 0px auto;
|
||||
}
|
||||
tr {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
}
|
||||
tr:hover td {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
th {
|
||||
text-align: left;
|
||||
font-size: .75em;
|
||||
padding-right: 20px;
|
||||
}
|
||||
/* 2nd Column: Filename */
|
||||
th + th {
|
||||
width: 65%;
|
||||
}
|
||||
/* 3rd Column: Last Modified */
|
||||
/* 4th Column: Size */
|
||||
th + th + th + th {
|
||||
width: 5%;
|
||||
}
|
||||
tr td:first-of-type {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
td {
|
||||
padding: 5px 0;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #edf1f5;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
-webkit-transition: background 300ms ease-in;
|
||||
-moz-transition: background 300ms ease-in;
|
||||
-ms-transition: background 300ms ease-in;
|
||||
-o-transition: background 300ms ease-in;
|
||||
transition: background 300ms ease-in;
|
||||
}
|
||||
td:last-child,
|
||||
th:last-child {
|
||||
text-align: right;
|
||||
padding-right: 5px;
|
||||
}
|
||||
td a {
|
||||
display: block;
|
||||
}
|
||||
tr.parent a {
|
||||
color: #9099A3;
|
||||
}
|
||||
.parent a:hover {
|
||||
color: #2a2a2a;
|
||||
}
|
||||
|
||||
/*------------------------------------*\
|
||||
Loading Indicator
|
||||
\*------------------------------------*/
|
||||
.signal {
|
||||
border: 2px solid #333;
|
||||
border-radius: 15px;
|
||||
height: 15px;
|
||||
left: 50%;
|
||||
margin: -8px 0 0 -8px;
|
||||
opacity: 0;
|
||||
top: 50%;
|
||||
width: 15px;
|
||||
float: right;
|
||||
animation: pulsate 1s ease-out;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
@keyframes pulsate {
|
||||
0% {
|
||||
transform: scale(.1);
|
||||
opacity: 0.0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------*\
|
||||
Footer
|
||||
\*------------------------------------*/
|
||||
.footer {
|
||||
text-align: center;
|
||||
font-size: .75em;
|
||||
margin-top: 50px;
|
||||
}
|
||||
img {
|
||||
outline: none;
|
||||
border: none;
|
||||
max-height: 16px;
|
||||
}
|
226
vendor/github.com/genuinetools/reg/server/static/css/styles.less
generated
vendored
Normal file
226
vendor/github.com/genuinetools/reg/server/static/css/styles.less
generated
vendored
Normal file
|
@ -0,0 +1,226 @@
|
|||
|
||||
@import url('http://fonts.googleapis.com/css?family=Open+Sans:400,300');
|
||||
/* Have to use @import for the font, as you can only specify a single stylesheet */
|
||||
* {
|
||||
margin:0;
|
||||
padding:0;
|
||||
-webkit-box-sizing:border-box;
|
||||
-moz-box-sizing:border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height:100%;
|
||||
border-top:10px solid #ECEEF1;
|
||||
border-bottom:10px solid #ECEEF1;
|
||||
color:#61666c;
|
||||
font-weight:300;
|
||||
font-size:1em;
|
||||
font-family:'Open Sans', sans-serif;
|
||||
line-height:2em;
|
||||
}
|
||||
body {
|
||||
padding:20px;
|
||||
-webkit-backface-visibility:hidden;
|
||||
}
|
||||
code {
|
||||
font-family:Inconsolata,monospace;
|
||||
}
|
||||
a {
|
||||
color:#61666c;
|
||||
text-decoration:none;
|
||||
}
|
||||
a:hover {
|
||||
color:#2a2a2a;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Wrapper
|
||||
\*------------------------------------*/
|
||||
.wrapper {
|
||||
margin:0 auto;
|
||||
padding-top:20px;
|
||||
max-width:800px;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Demo block
|
||||
\*------------------------------------*/
|
||||
.block {
|
||||
font-size:.875em;
|
||||
margin:20px 0;
|
||||
padding:20px;
|
||||
color:#9099A3;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight:200;
|
||||
text-align:center;
|
||||
font-size:1.4em;
|
||||
line-height:3em;
|
||||
font-family:'Museo Slab','Open Sans',monospace;
|
||||
}
|
||||
form {
|
||||
text-align:center;
|
||||
}
|
||||
input {
|
||||
margin: 0 auto;
|
||||
font-size: 100%;
|
||||
vertical-align: middle;
|
||||
*overflow: visible;
|
||||
line-height: normal;
|
||||
font-family:'Open Sans', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
color:#555555;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
padding: 4px 32px 4px 6px;
|
||||
margin-bottom: 9px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #555555;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
width: 196px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-webkit-transition: border linear .2s, box-shadow linear .2s;
|
||||
-moz-transition: border linear .2s, box-shadow linear .2s;
|
||||
-o-transition: border linear .2s, box-shadow linear .2s;
|
||||
transition: border linear .2s, box-shadow linear .2s;
|
||||
background: url('search.svg') no-repeat 211px center;
|
||||
background-size:auto 20px;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
outline: 0;
|
||||
outline: thin dotted \9;
|
||||
/* IE6-9 */
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
}
|
||||
|
||||
input::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
input[type="search"] {
|
||||
margin-top: 20px;
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
-webkit-appearance: textfield;
|
||||
-webkit-transition:all 300ms ease-in;
|
||||
-moz-transition:all 300ms ease-in;
|
||||
-ms-transition:all 300ms ease-in;
|
||||
-o-transition:all 300ms ease-in;
|
||||
transition:all 300ms ease-in;
|
||||
}
|
||||
|
||||
input[type="search"]::-webkit-search-decoration,
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
a.clear, a.clear:link, a.clear:visited {
|
||||
color:#666;
|
||||
padding:2px 0 2px 0;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
margin:0px 0 0 20px;
|
||||
line-height: 14px;
|
||||
display: inline-block;
|
||||
border-bottom: transparent 1px solid;
|
||||
vertical-align: -10px;
|
||||
-webkit-transition:all 300ms ease-in;
|
||||
-moz-transition:all 300ms ease-in;
|
||||
-ms-transition:all 300ms ease-in;
|
||||
-o-transition:all 300ms ease-in;
|
||||
transition:all 300ms ease-in;
|
||||
}
|
||||
a.clear:hover {
|
||||
text-decoration: none;
|
||||
color:#333;
|
||||
cursor: pointer;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Table (directory listing)
|
||||
\*------------------------------------*/
|
||||
table {
|
||||
border-collapse:collapse;
|
||||
font-size:.875em;
|
||||
max-width:100%;
|
||||
margin:20px auto 0px auto;
|
||||
}
|
||||
tr {
|
||||
outline:0;
|
||||
border:0;
|
||||
}
|
||||
tr:hover td {
|
||||
background:#f6f6f6;
|
||||
}
|
||||
th {
|
||||
text-align:left;
|
||||
font-size:.75em;
|
||||
padding-right:20px;
|
||||
}
|
||||
/* 2nd Column: Filename */
|
||||
th + th {
|
||||
width:65%;
|
||||
}
|
||||
/* 3rd Column: Last Modified */
|
||||
th + th + th {
|
||||
}
|
||||
/* 4th Column: Size */
|
||||
th + th + th + th {
|
||||
width:5%;
|
||||
}
|
||||
tr td:first-of-type {
|
||||
padding-left:10px;
|
||||
padding-right:10px;
|
||||
}
|
||||
td {
|
||||
padding:5px 0;
|
||||
outline:0;
|
||||
border:0;
|
||||
border-bottom:1px solid #edf1f5;
|
||||
vertical-align:middle;
|
||||
text-align:left;
|
||||
-webkit-transition:background 300ms ease-in;
|
||||
-moz-transition:background 300ms ease-in;
|
||||
-ms-transition:background 300ms ease-in;
|
||||
-o-transition:background 300ms ease-in;
|
||||
transition:background 300ms ease-in;
|
||||
}
|
||||
td:last-child, th:last-child {
|
||||
text-align:right;
|
||||
padding-right:0px;
|
||||
}
|
||||
td a{
|
||||
display: block;
|
||||
}
|
||||
tr.parent a {
|
||||
color:#9099A3;
|
||||
}
|
||||
.parent a:hover {
|
||||
color:#2a2a2a;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Footer
|
||||
\*------------------------------------*/
|
||||
.footer {
|
||||
text-align:center;
|
||||
font-size:.75em;
|
||||
margin-top:50px;
|
||||
}
|
||||
img {
|
||||
outline:none;
|
||||
border:none;
|
||||
max-height:16px;
|
||||
}
|
BIN
vendor/github.com/genuinetools/reg/server/static/favicon.ico
generated
vendored
Normal file
BIN
vendor/github.com/genuinetools/reg/server/static/favicon.ico
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
5
vendor/github.com/genuinetools/reg/server/static/js/lib/jquery-1.9.1.min.js
generated
vendored
Normal file
5
vendor/github.com/genuinetools/reg/server/static/js/lib/jquery-1.9.1.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
103
vendor/github.com/genuinetools/reg/server/static/js/scripts.js
generated
vendored
Normal file
103
vendor/github.com/genuinetools/reg/server/static/js/scripts.js
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
// pretty date function
|
||||
function prettyDate(time){
|
||||
var date = new Date((time || "").replace(/-/g,"/").replace(/[TZ]/g," ")),
|
||||
diff = (((new Date()).getTime() - date.getTime()) / 1000),
|
||||
day_diff = Math.floor(diff / 86400);
|
||||
|
||||
if (isNaN(day_diff) || day_diff < 0)
|
||||
return;
|
||||
|
||||
return day_diff == 0 && (
|
||||
diff < 60 && "just now" ||
|
||||
diff < 120 && "1 minute ago" ||
|
||||
diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
|
||||
diff < 7200 && "1 hour ago" ||
|
||||
diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
|
||||
day_diff == 1 && "Yesterday" ||
|
||||
day_diff < 7 && day_diff + " days ago" ||
|
||||
day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago" ||
|
||||
day_diff > 31 && Math.round(day_diff / 31) + " months ago";
|
||||
}
|
||||
|
||||
// search function
|
||||
function search(search_val){
|
||||
var suche = search_val.toLowerCase();
|
||||
var table = document.getElementById("directory");
|
||||
var cellNr = 1;
|
||||
var ele;
|
||||
for (var r = 1; r < table.rows.length; r++){
|
||||
ele = table.rows[r].cells[cellNr].innerHTML.replace(/<[^>]+>/g,"");
|
||||
if (ele.toLowerCase().indexOf(suche)>=0 ) {
|
||||
table.rows[r].style.display = '';
|
||||
} else {
|
||||
table.rows[r].style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadVulnerabilityCount(url){
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url);
|
||||
xhr.onload = function() {
|
||||
if (xhr.status === 200) {
|
||||
var report = JSON.parse(xhr.responseText);
|
||||
var id = report.Repo + ':' + report.Tag;
|
||||
var element = document.getElementById(id);
|
||||
|
||||
if (element) {
|
||||
element.innerHTML = report.BadVulns;
|
||||
} else {
|
||||
console.log("element not found for given id ", id);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
var el = document.querySelectorAll('tr:nth-child(2)')[0].querySelectorAll('td:nth-child(2)')[0];
|
||||
if (el.textContent == 'Parent Directory'){
|
||||
var parent_row = document.querySelectorAll('tr:nth-child(2)')[0];
|
||||
if (parent_row.classList){
|
||||
parent_row.classList.add('parent');
|
||||
} else {
|
||||
parent_row.className += ' ' + 'parent';
|
||||
}
|
||||
}
|
||||
|
||||
var cells = document.querySelectorAll('td a');
|
||||
Array.prototype.forEach.call(cells, function(item, index){
|
||||
var link = item.getAttribute('href');
|
||||
link = link.replace('.html', '');
|
||||
item.setAttribute('href', link);
|
||||
});
|
||||
|
||||
var our_table = document.querySelectorAll('table')[0];
|
||||
our_table.setAttribute('id', 'directory');
|
||||
|
||||
// search script
|
||||
var search_input = document.querySelectorAll('input[name="filter"]')[0];
|
||||
var clear_button = document.querySelectorAll('a.clear')[0];
|
||||
|
||||
if (search_input) {
|
||||
if (search_input.value !== ''){
|
||||
search(search_input.value);
|
||||
}
|
||||
|
||||
search_input.addEventListener('keyup', function(e){
|
||||
e.preventDefault();
|
||||
search(search_input.value);
|
||||
});
|
||||
|
||||
search_input.addEventListener('keypress', function(e){
|
||||
if ( e.which == 13 ) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (clear_button) {
|
||||
clear_button.addEventListener('click', function(e){
|
||||
search_input.value = '';
|
||||
search('');
|
||||
});
|
||||
}
|
60
vendor/github.com/genuinetools/reg/server/templates/repositories.html
generated
vendored
Normal file
60
vendor/github.com/genuinetools/reg/server/templates/repositories.html
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
{{define "repositories"}}
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="/" >
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>{{ .RegistryDomain }}</title>
|
||||
<link rel="icon" type="image/ico" href="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ .RegistryDomain }}</h1>
|
||||
<form>
|
||||
<input name="filter" type="search"><a class="clear">clear</a>
|
||||
</form>
|
||||
|
||||
<div class="wrapper">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Repository Name</th>
|
||||
<th>Pull Command</th>
|
||||
</tr>
|
||||
{{ range $key, $value := .Repositories }}
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tags">
|
||||
{{ $value.Name }}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td align="right" nowrap>
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tags">
|
||||
<code>docker pull {{ $value.URI }}</code>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a href="https://twitter.com/jessfraz">@jessfraz</a>
|
||||
<p>Last Updated: {{ .LastUpdated }}</p>
|
||||
</div><!--/.footer-->
|
||||
<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),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-29404280-12', 'jessfraz.com');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
77
vendor/github.com/genuinetools/reg/server/templates/tags.html
generated
vendored
Normal file
77
vendor/github.com/genuinetools/reg/server/templates/tags.html
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
{{define "tags"}}
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-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="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ .RegistryDomain }}/{{ .Name }}</h1>
|
||||
<div class="wrapper">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Tag</th>
|
||||
<th>Created</th>
|
||||
<th>Vulnerabilities</th>
|
||||
</tr>
|
||||
{{ range $key, $value := .Repositories }}
|
||||
<tr>
|
||||
<td valign="left" nowrap>
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns">
|
||||
{{ $value.Name }}
|
||||
</a>
|
||||
</td>
|
||||
<td align="right" nowrap>
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns">
|
||||
{{ $value.Tag }}
|
||||
</a>
|
||||
</td>
|
||||
<td align="right" nowrap>
|
||||
{{ $value.Created.Format "02 Jan, 2006 15:04:05 UTC" }}
|
||||
</td>
|
||||
<td align="right" nowrap>
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns" id="{{ $value.Name }}:{{ $value.Tag }}">
|
||||
<div class="signal"></div>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a href="https://twitter.com/jessfraz">@jessfraz</a>
|
||||
<p>Last Updated: {{ .LastUpdated }}</p>
|
||||
</div><!--/.footer-->
|
||||
<script src="/static/js/scripts.js"></script>
|
||||
<script type="text/javascript">
|
||||
var ajaxCalls = [
|
||||
{{ range $key, $value := .Repositories }}
|
||||
'/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns.json',
|
||||
{{ end }}
|
||||
];
|
||||
window.onload = function() {
|
||||
Array.prototype.forEach.call(ajaxCalls, function(url, index){
|
||||
loadVulnerabilityCount(url);
|
||||
});
|
||||
};
|
||||
</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),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-29404280-12', 'jessfraz.com');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
78
vendor/github.com/genuinetools/reg/server/templates/vulns.html
generated
vendored
Normal file
78
vendor/github.com/genuinetools/reg/server/templates/vulns.html
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
{{define "vulns"}}
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-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="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="/">{{ .RegistryURL }}</a></li>
|
||||
<li class="active">{{ .Repo }}:{{ .Tag }}</li>
|
||||
</ol>
|
||||
|
||||
<div class="page-header">
|
||||
<h1>{{ .RegistryURL }}/{{ .Repo }}:{{ .Tag }} <small>Vulnerability Report</small></h1>
|
||||
</div>
|
||||
<p class="text-right">Generated on: {{.Date}}</p>
|
||||
|
||||
{{if gt .BadVulns 5}}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{.BadVulns}} High, Critical, and/or Defcon1 vulnerabilities found
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<h2>Summary</h2>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ len .Vulns }}</span>
|
||||
<b>Total</b>
|
||||
</li>
|
||||
{{range $key, $value := .VulnsBySeverity}}
|
||||
<li class="list-group-item">
|
||||
<span class="label label-{{color $key}} pull-right">{{ len $value }}</span>
|
||||
{{ $key }}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
<h2>Details</h2>
|
||||
{{range $vulns := .VulnsBySeverity}}
|
||||
{{range $value := $vulns}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{{$value.Name}}
|
||||
<span class="label label-{{color $value.Severity}} pull-right">{{$value.Severity}}</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{$value.Description}}
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<a href="{{$value.Link}}" target="_blank">{{$value.Link}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
|
||||
<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),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-29404280-12', 'jessfraz.com');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
15
vendor/github.com/genuinetools/reg/server/templates/vulns.txt
generated
vendored
Normal file
15
vendor/github.com/genuinetools/reg/server/templates/vulns.txt
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{{define "vulns"}}CVE Report for {{.Repo}}:{{.Tag}}
|
||||
Generated on: {{.Date}}
|
||||
|
||||
Vulnerabilities Found: {{len .Vulns}}
|
||||
|
||||
{{range $key, $value := .VulnsBySeverity}}{{$key}}: {{len $value}}
|
||||
{{end}}
|
||||
{{if gt .BadVulns 5}}------------------------------------ ALERT ------------------------------------
|
||||
{{.BadVulns}} High, Critical, and/or Defcon1 vulnerabilities found
|
||||
|
||||
{{end}}{{range $vulns := .VulnsBySeverity}}{{range $value := $vulns}}{{$value.Name}}: [{{$value.Severity}}]
|
||||
{{trim $value.Description}}
|
||||
{{$value.Link}}
|
||||
-------------------------------------------------------------------------------
|
||||
{{end}}{{end}}{{end}}
|
BIN
vendor/github.com/genuinetools/reg/server/vuln.png
generated
vendored
Normal file
BIN
vendor/github.com/genuinetools/reg/server/vuln.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
23
vendor/github.com/genuinetools/reg/testutils/configs/basicauth.yml
generated
vendored
Normal file
23
vendor/github.com/genuinetools/reg/testutils/configs/basicauth.yml
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
version: 0.1
|
||||
log:
|
||||
level: debug
|
||||
formatter: text
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
delete:
|
||||
enabled: true
|
||||
http:
|
||||
addr: 0.0.0.0:5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
host: https://localhost:5000
|
||||
tls:
|
||||
certificate: /etc/docker/registry/ssl/cert.pem
|
||||
key: /etc/docker/registry/ssl/key.pem
|
||||
auth:
|
||||
htpasswd:
|
||||
realm: basic-realm
|
||||
path: /etc/docker/registry/htpasswd
|
1
vendor/github.com/genuinetools/reg/testutils/configs/htpasswd
generated
vendored
Normal file
1
vendor/github.com/genuinetools/reg/testutils/configs/htpasswd
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
admin:$2y$05$TpcLzA8b5hMLptNGRIRWXuOI7KmAOqIuRhHAv15qHNrJaxuyIhCg6
|
19
vendor/github.com/genuinetools/reg/testutils/configs/noauth.yml
generated
vendored
Normal file
19
vendor/github.com/genuinetools/reg/testutils/configs/noauth.yml
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
version: 0.1
|
||||
log:
|
||||
level: debug
|
||||
formatter: text
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
delete:
|
||||
enabled: true
|
||||
http:
|
||||
addr: 0.0.0.0:5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
host: https://localhost:5000
|
||||
tls:
|
||||
certificate: /etc/docker/registry/ssl/cert.pem
|
||||
key: /etc/docker/registry/ssl/key.pem
|
19
vendor/github.com/genuinetools/reg/testutils/snakeoil/cert.pem
generated
vendored
Normal file
19
vendor/github.com/genuinetools/reg/testutils/snakeoil/cert.pem
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDAjCCAeqgAwIBAgIQC+Tw335jnu9Z46unSVFfeTANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMB4XDTE3MDYwNjAwMDAyNFoXDTE4MDYwNjAwMDAy
|
||||
NFowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBAN8/Xqef2iozUMXLRCvHnGc+SMaEDuW/iM9n/IL68F11esAR6tXhaiRQ
|
||||
RdEsM9MyfyDYt9juE+XLaMyqhTAXwK9YzULE4BTVbAr9uOgxLtyWspA4uWfxhcrq
|
||||
CqQTRc0U95ZEnAVSjytDAXtLQyP+UlnMzmMhDpzRuH1YXqm6qB07G5yOJyPKkDrq
|
||||
EyqUsjqBJmBkUJiYfSx95Jen+4ZzlSR7wOsoxei09QYyvo3DMfcJO8Wb8IQFjOT0
|
||||
ohhBFBR3v1HOOT6bKuhUHif3K+STguMEhHrgAFmcFW3NPQ3It2SyKfGBZ8nbmSA9
|
||||
2tjojQEFUHqRKp0UWyObuUmNAo1U1w8CAwEAAaNUMFIwDgYDVR0PAQH/BAQDAgKk
|
||||
MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wGgYDVR0RBBMw
|
||||
EYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCl+yJgekN3dMbR
|
||||
mxs7B8VSFv5a7zElDvTnagX3pSBHq7fLf45qVHOmZK/esgD3K8H5Kvft6u100b1j
|
||||
4TLn2oFYVMME8BV0qNl5wgynNrJs131G3jgxcrgqu9NdlFpWZm8S+DCHo+h1ZH4Z
|
||||
LmlUt2uvwCbmZK/e6U0ZDICDKRwMaC6LdUCfLfn9F+ACpPTpAZBVbi0rpAYimBDf
|
||||
j2QZJBWD9tV5xUVSLEmqFvi42g2khK43HFu9N35vPIqyrl4Gh5x3erZR7t8pGEu0
|
||||
kOiqfCmV1GHL8egxYew4wJ1P6TzhYNk/7vpiJxrwPs3vW+WXaSBFdvoV1qQ6onjm
|
||||
CxG31l+z
|
||||
-----END CERTIFICATE-----
|
27
vendor/github.com/genuinetools/reg/testutils/snakeoil/key.pem
generated
vendored
Normal file
27
vendor/github.com/genuinetools/reg/testutils/snakeoil/key.pem
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA3z9ep5/aKjNQxctEK8ecZz5IxoQO5b+Iz2f8gvrwXXV6wBHq
|
||||
1eFqJFBF0Swz0zJ/INi32O4T5ctozKqFMBfAr1jNQsTgFNVsCv246DEu3JaykDi5
|
||||
Z/GFyuoKpBNFzRT3lkScBVKPK0MBe0tDI/5SWczOYyEOnNG4fVheqbqoHTsbnI4n
|
||||
I8qQOuoTKpSyOoEmYGRQmJh9LH3kl6f7hnOVJHvA6yjF6LT1BjK+jcMx9wk7xZvw
|
||||
hAWM5PSiGEEUFHe/Uc45Ppsq6FQeJ/cr5JOC4wSEeuAAWZwVbc09Dci3ZLIp8YFn
|
||||
yduZID3a2OiNAQVQepEqnRRbI5u5SY0CjVTXDwIDAQABAoIBACw3LNQeQONiznie
|
||||
TZ4uJrf8CgXnWdv/F2WcvtJiSQD5p5oq8kvyHUeb7ngDPTBzK+KhiagZXy+AHf2L
|
||||
OF3SFoOkHuM+gvMdYgy7O8ghFZry7eLKmU4Q8+LAf+MHPifkIzVL2Wrkcx6qYry8
|
||||
p0uVr1HB0o6nmXFNyDBrNDSBl5JSJ+IyvtPr7ow5iO2iZLh0nV5CfM65vX2ESkvD
|
||||
+uil1uFfLdmNkfItK/0oTLngiXzJBCgPzTwBnKGlmoYzWvO/CMMIEkv0tdy/b1l9
|
||||
BTXiuRQEBy+CzSmoPyNnBCE/SOhZLH8+eGzsnlaty66AKWA1EwSjq7lDO1MOAL5Y
|
||||
dPqwk8ECgYEA51T895d707Hl2/ggpEP1Wg8p96nz2iCt903WIOLn2X/9su2mNu5r
|
||||
+Xtkd39ZYUuJIIja8hyx3q9E761jSI/F3lr4jwhTYO15aNvyD9S0supqASVodT33
|
||||
VKYxrFH2PbRfb7RyuTUjlusJeP0QFz7hZ81eooYcgqkv8Gim14Y6XgkCgYEA9w2w
|
||||
P5bTEPHweGCJ8I9AgCGUsg39x23qwN8xkxKb6jQcj6wHBpSw/yAPpAZ/1o+GFWDO
|
||||
xiiNfqc+pLHAgPwEY2iUFKKJtKaS1kFIljTK353HVrDcqviCa4GCpSlBRi4xBkfi
|
||||
vxS81eKaf8ChoiqfOuz3g6dHl6n3RGoQ8KpgUlcCgYBzbJh8AX2rdww14WyICdCW
|
||||
CxLpnEcsAzpKNvAsoIsGnzI64REaP4RoiwTqCwTR4xqcvSxhuaeWcOV4oY70Wahk
|
||||
9gcndwQDTPpTM8tn0r4Gt6gEwmGIfk62UeZfENZIm4My/Vpwxu7nEoc7cylgL+PQ
|
||||
I0yg00HOgBSHY/A7gaIF4QKBgQDwjfaQZEaOGFYCkFWf04yFdq03lmIF/qP3Oxwl
|
||||
TZhdOnKY/nM02DFjqY8xMlblz4hKZqHP1wq3SRe4+48qyLlpJhoR4ZXePdd6IcUQ
|
||||
5MSpahL/+WRUYXd0QH26Xeo98JoxuGszjXi1dljjjeiUY5X5pWT4XzhZl9i5V+G4
|
||||
xNzXLwKBgHtH/cPeR5O5gSHG+Fi5Sb/Ip6YYg00N8vtGwYYyc2i/uqz1N20igHJY
|
||||
df7D5eYRIqrhBUVxqaqqs43oa1fi7CIFITYmof+qpxzRWKq9PPFc8D9mV0/03lba
|
||||
0+i0kAvJB76WBiX48z8h+Rbc0IrZRDrVz9fk4Yfh+gHT4KDPmuII
|
||||
-----END RSA PRIVATE KEY-----
|
241
vendor/github.com/genuinetools/reg/testutils/testutils.go
generated
vendored
Normal file
241
vendor/github.com/genuinetools/reg/testutils/testutils.go
generated
vendored
Normal file
|
@ -0,0 +1,241 @@
|
|||
package testutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
)
|
||||
|
||||
// StartRegistry starts a new registry container.
|
||||
func StartRegistry(dcli *client.Client, config, username, password string) (string, string, error) {
|
||||
_, filename, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
return "", "", errors.New("No caller information")
|
||||
}
|
||||
|
||||
image := "registry:2"
|
||||
|
||||
if err := pullDockerImage(dcli, image); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
r, err := dcli.ContainerCreate(
|
||||
context.Background(),
|
||||
&container.Config{
|
||||
Image: image,
|
||||
},
|
||||
&container.HostConfig{
|
||||
NetworkMode: "host",
|
||||
Binds: []string{
|
||||
filepath.Join(filepath.Dir(filename), "configs", config) + ":" + "/etc/docker/registry/config.yml" + ":ro",
|
||||
filepath.Join(filepath.Dir(filename), "configs", "htpasswd") + ":" + "/etc/docker/registry/htpasswd" + ":ro",
|
||||
filepath.Join(filepath.Dir(filename), "snakeoil") + ":" + "/etc/docker/registry/ssl" + ":ro",
|
||||
},
|
||||
},
|
||||
nil, "")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// start the container
|
||||
if err := dcli.ContainerStart(context.Background(), r.ID, types.ContainerStartOptions{}); err != nil {
|
||||
return r.ID, "", err
|
||||
}
|
||||
|
||||
port := ":5000"
|
||||
addr := "https://localhost" + port
|
||||
|
||||
if err := waitForConn(addr, filepath.Join(filepath.Dir(filename), "snakeoil", "cert.pem"), filepath.Join(filepath.Dir(filename), "snakeoil", "key.pem")); err != nil {
|
||||
return r.ID, addr, err
|
||||
}
|
||||
|
||||
if err := dockerLogin("localhost"+port, username, password); err != nil {
|
||||
return r.ID, addr, err
|
||||
}
|
||||
|
||||
// Prefill the images.
|
||||
images := []string{"alpine:latest", "busybox:latest", "busybox:musl", "busybox:glibc"}
|
||||
for _, image := range images {
|
||||
if err := prefillRegistry(image, dcli, "localhost"+port, username, password); err != nil {
|
||||
return r.ID, addr, err
|
||||
}
|
||||
}
|
||||
|
||||
return r.ID, addr, nil
|
||||
}
|
||||
|
||||
// RemoveContainer removes with force a container by it's container ID.
|
||||
func RemoveContainer(dcli *client.Client, ctrID string) error {
|
||||
return dcli.ContainerRemove(context.Background(), ctrID,
|
||||
types.ContainerRemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
// dockerLogin logins via the command line to a docker registry
|
||||
func dockerLogin(addr, username, password string) error {
|
||||
cmd := exec.Command("docker", "login", "--username", username, "--password", password, addr)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("docker login [%s] failed with output %q and error: %v", strings.Join(cmd.Args, " "), string(out), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// prefillRegistry adds images to a registry.
|
||||
func prefillRegistry(image string, dcli *client.Client, addr, username, password string) error {
|
||||
if err := pullDockerImage(dcli, image); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dcli.ImageTag(context.Background(), image, addr+"/"+image); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auth, err := constructRegistryAuth(username, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := dcli.ImagePush(context.Background(), addr+"/"+image, types.ImagePushOptions{
|
||||
RegistryAuth: auth,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
fd, isTerm := term.GetFdInfo(os.Stdout)
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerm, nil)
|
||||
}
|
||||
|
||||
func pullDockerImage(dcli *client.Client, image string) error {
|
||||
exists, err := imageExists(dcli, image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := dcli.ImagePull(context.Background(), image, types.ImagePullOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
fd, isTerm := term.GetFdInfo(os.Stdout)
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerm, nil)
|
||||
}
|
||||
|
||||
func imageExists(dcli *client.Client, image string) (bool, error) {
|
||||
_, _, err := dcli.ImageInspectWithRaw(context.Background(), image)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if client.IsErrNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// waitForConn takes a tcp addr and waits until it is reachable
|
||||
func waitForConn(addr, cert, key string) error {
|
||||
tlsCert, err := tls.LoadX509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not load X509 key pair: %v. Make sure the key is not encrypted", err)
|
||||
}
|
||||
|
||||
certPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read system certificates: %v", err)
|
||||
}
|
||||
pem, err := ioutil.ReadFile(cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read CA certificate %s: %v", cert, err)
|
||||
}
|
||||
if !certPool.AppendCertsFromPEM(pem) {
|
||||
return fmt.Errorf("failed to append certificates from PEM file: %s", cert)
|
||||
}
|
||||
|
||||
c := http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
},
|
||||
RootCAs: certPool,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
n := 0
|
||||
max := 10
|
||||
for n < max {
|
||||
if _, err := c.Get(addr + "/v2/"); err != nil {
|
||||
fmt.Printf("try number %d to %s: %v\n", n, addr, err)
|
||||
n++
|
||||
if n != max {
|
||||
fmt.Println("sleeping for 1 second then will try again...")
|
||||
time.Sleep(time.Second)
|
||||
} else {
|
||||
return fmt.Errorf("[WHOOPS]: maximum retries for %s exceeded", addr)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// constructRegistryAuth serializes the auth configuration as JSON base64 payload.
|
||||
func constructRegistryAuth(identity, secret string) (string, error) {
|
||||
buf, err := json.Marshal(types.AuthConfig{Username: identity, Password: secret})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.URLEncoding.EncodeToString(buf), nil
|
||||
}
|
7
vendor/github.com/genuinetools/reg/version/version.go
generated
vendored
Normal file
7
vendor/github.com/genuinetools/reg/version/version.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package version
|
||||
|
||||
// VERSION indicates which version of the binary is running.
|
||||
var VERSION string
|
||||
|
||||
// GITCOMMIT indicates which git hash the binary was built off of
|
||||
var GITCOMMIT string
|
Loading…
Add table
Add a link
Reference in a new issue