From 3c31d7ad141451d87f3667ed06ce96793c321f41 Mon Sep 17 00:00:00 2001
From: Segev Finer
Date: Fri, 23 Dec 2016 13:32:28 +0200
Subject: [PATCH 001/160] ldap_auth: An empty password worked for any valid
user (#155)
See: https://github.com/go-ldap/ldap/issues/93
---
auth_server/authn/ldap_auth.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go
index a885b40c..38ec9dad 100644
--- a/auth_server/authn/ldap_auth.go
+++ b/auth_server/authn/ldap_auth.go
@@ -54,7 +54,7 @@ func NewLDAPAuth(c *LDAPAuthConfig) (*LDAPAuth, error) {
//How to authenticate user, please refer to https://github.com/go-ldap/ldap/blob/master/example_test.go#L166
func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, Labels, error) {
- if account == "" {
+ if account == "" || password == "" {
return false, nil, NoMatch
}
l, err := la.ldapConnection()
From 1f6471f438d4efe440473e6b61f823e14ebe846f Mon Sep 17 00:00:00 2001
From: Jan Schmitz-Hermes
Date: Fri, 23 Dec 2016 19:09:08 +0100
Subject: [PATCH 002/160] Feature: GHE Support (#151)
* Parametrize GitHub host, for use with GHE
---
auth_server/authn/data/github_auth.tmpl | 4 ++--
auth_server/authn/github_auth.go | 25 ++++++++++++++++++++++---
examples/reference.yml | 6 ++++++
3 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/auth_server/authn/data/github_auth.tmpl b/auth_server/authn/data/github_auth.tmpl
index 9fb86871..05daa798 100644
--- a/auth_server/authn/data/github_auth.tmpl
+++ b/auth_server/authn/data/github_auth.tmpl
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/auth_server/authn/github_auth.go b/auth_server/authn/github_auth.go
index 9bef12b2..49e58965 100644
--- a/auth_server/authn/github_auth.go
+++ b/auth_server/authn/github_auth.go
@@ -39,6 +39,8 @@ type GitHubAuthConfig struct {
TokenDB string `yaml:"token_db,omitempty"`
HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"`
+ GithubWebUri string `yaml:"github_web_uri,omitempty"`
+ GithubApiUri string `yaml:"github_api_uri,omitempty"`
}
type GitHubAuthRequest struct {
@@ -90,13 +92,30 @@ func (gha *GitHubAuth) DoGitHubAuth(rw http.ResponseWriter, req *http.Request) {
}
}
+func (gha *GitHubAuth) getGithubApiUri() string {
+ if gha.config.GithubApiUri != "" {
+ return gha.config.GithubApiUri
+ } else {
+ return "https://api.github.com/"
+ }
+}
+
+func (gha *GitHubAuth) getGithubWebUri() string {
+ if gha.config.GithubWebUri != "" {
+ return gha.config.GithubWebUri
+ } else {
+ return "https://github.com/"
+ }
+}
+
func (gha *GitHubAuth) doGitHubAuthCreateToken(rw http.ResponseWriter, code string) {
data := url.Values{
"code": []string{string(code)},
"client_id": []string{gha.config.ClientId},
"client_secret": []string{gha.config.ClientSecret},
}
- req, err := http.NewRequest("POST", "https://github.com/login/oauth/access_token", bytes.NewBufferString(data.Encode()))
+
+ req, err := http.NewRequest("POST", fmt.Sprintf("%s/login/oauth/access_token", gha.getGithubWebUri()), bytes.NewBufferString(data.Encode()))
if err != nil {
http.Error(rw, fmt.Sprintf("Error creating request to GitHub auth backend: %s", err), http.StatusServiceUnavailable)
return
@@ -150,7 +169,7 @@ func (gha *GitHubAuth) doGitHubAuthCreateToken(rw http.ResponseWriter, code stri
}
func (gha *GitHubAuth) validateAccessToken(token string) (user string, err error) {
- req, err := http.NewRequest("GET", "https://api.github.com/user", nil)
+ req, err := http.NewRequest("GET", fmt.Sprintf("%s/user", gha.getGithubApiUri()), nil)
if err != nil {
err = fmt.Errorf("could not create request to get information for token %s: %s", token, err)
return
@@ -187,7 +206,7 @@ func (gha *GitHubAuth) checkOrganization(token, user string) (err error) {
if gha.config.Organization == "" {
return nil
}
- url := fmt.Sprintf("https://api.github.com/orgs/%s/members/%s", gha.config.Organization, user)
+ url := fmt.Sprintf("%s/orgs/%s/members/%s", gha.getGithubApiUri(), gha.config.Organization, user)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
err = fmt.Errorf("could not create request to get organization membership: %s", err)
diff --git a/examples/reference.yml b/examples/reference.yml
index 8737bf60..716a8d01 100644
--- a/examples/reference.yml
+++ b/examples/reference.yml
@@ -87,6 +87,12 @@ github_auth:
http_timeout: "10s"
# How long to wait before revalidating the GitHub token. Optional.
revalidate_after: "1h"
+ # The Github Web URI in case you are using Github Enterprise.
+ # Includes the protocol, without trailing slash. Optional - defaults to: https://github.com
+ github_web_uri: "https://github.acme.com/"
+ # The Github API URI in case you are using Github Enterprise.
+ # Includes the protocol, without trailing slash. - defaults to: https://api.github.com
+ github_api_uri: "https://github.acme.com/api/v3"
# LDAP authentication.
# Authentication is performed by first binding to the server, looking up the user entry
From 99a73068f45feac016c874db8a1deb795b94ae02 Mon Sep 17 00:00:00 2001
From: rojer
Date: Fri, 23 Dec 2016 18:25:32 +0000
Subject: [PATCH 003/160] Update bindata
---
auth_server/authn/bindata.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/auth_server/authn/bindata.go b/auth_server/authn/bindata.go
index 08b0104b..66db1f55 100644
--- a/auth_server/authn/bindata.go
+++ b/auth_server/authn/bindata.go
@@ -47,8 +47,8 @@ func (fi bindataFileInfo) Sys() interface{} {
var _dataGithub_authTmpl = []byte(`
-
-
+
+
`)
@@ -63,7 +63,7 @@ func dataGithub_authTmpl() (*asset, error) {
return nil, err
}
- info := bindataFileInfo{name: "data/github_auth.tmpl", size: 350, mode: os.FileMode(420), modTime: time.Unix(1, 0)}
+ info := bindataFileInfo{name: "data/github_auth.tmpl", size: 348, mode: os.FileMode(420), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
From cfe79795a6fbb0365caba3ac4e3bbe4b1cf423f7 Mon Sep 17 00:00:00 2001
From: Webb Lu
Date: Wed, 4 Jan 2017 01:19:00 +0800
Subject: [PATCH 004/160] replace #!/bin/bash by #!/bin/sh, because bash not
exist in the :stable image (#159)
---
examples/ext_auth.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/ext_auth.sh b/examples/ext_auth.sh
index e5a17063..ed434857 100755
--- a/examples/ext_auth.sh
+++ b/examples/ext_auth.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
#
# Example external authenticator program for use with `ext_auth`.
#
From 69c6eeefe96cb9fb1cc7fe44fdec576cdea9218f Mon Sep 17 00:00:00 2001
From: Marc MILLIEN
Date: Tue, 3 Jan 2017 18:19:40 +0100
Subject: [PATCH 005/160] Quick fix so user knows that he should set registry
fqdn (#158)
Ref.: https://github.com/cesanta/docker_auth/issues/157
Say `docker login YOUR_REGISTRY_FQDN` as a quick n dirty fix so the user knows that he should set its registry url.
---
auth_server/authn/github_auth.go | 2 +-
auth_server/authn/google_auth.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/auth_server/authn/github_auth.go b/auth_server/authn/github_auth.go
index 49e58965..3a3de558 100644
--- a/auth_server/authn/github_auth.go
+++ b/auth_server/authn/github_auth.go
@@ -165,7 +165,7 @@ func (gha *GitHubAuth) doGitHubAuthCreateToken(rw http.ResponseWriter, code stri
return
}
- fmt.Fprintf(rw, `Server logged in; now run "docker login", use %s as login and %s as password.`, user, dp)
+ fmt.Fprintf(rw, `Server logged in; now run "docker login YOUR_REGISTRY_FQDN", use %s as login and %s as password.`, user, dp)
}
func (gha *GitHubAuth) validateAccessToken(token string) (user string, err error) {
diff --git a/auth_server/authn/google_auth.go b/auth_server/authn/google_auth.go
index cd0704cb..93f1a94a 100644
--- a/auth_server/authn/google_auth.go
+++ b/auth_server/authn/google_auth.go
@@ -234,7 +234,7 @@ func (ga *GoogleAuth) doGoogleAuthCreateToken(rw http.ResponseWriter, code strin
return
}
- fmt.Fprintf(rw, `Server logged in; now run "docker login", use %s as login and %s as password.`, user, dp)
+ fmt.Fprintf(rw, `Server logged in; now run "docker login YOUR_REGISTRY_FQDN", use %s as login and %s as password.`, user, dp)
}
func (ga *GoogleAuth) getIDTokenInfo(token string) (*GoogleTokenInfo, error) {
From 7568942f021f0e2899de03975b28fcb715c23b5b Mon Sep 17 00:00:00 2001
From: rojer
Date: Thu, 26 Jan 2017 11:21:39 +0000
Subject: [PATCH 006/160] Update README
---
README.md | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 4fe0513a..f499c55f 100644
--- a/README.md
+++ b/README.md
@@ -15,15 +15,21 @@ Supported authentication methods:
* Google Sign-In (incl. Google for Work / GApps for domain) (documented [here](https://github.com/cesanta/docker_auth/blob/master/examples/reference.yml))
* LDAP bind ([demo](https://github.com/kwk/docker-registry-setup))
* MongoDB user collection
- * External program
+ * [External program](https://github.com/cesanta/docker_auth/blob/master/examples/ext_auth.sh)
Supported authorization methods:
* Static ACL
* MongoDB-backed ACL
+ * External program
## Installation and Examples
-A public Docker image is available on Docker Hub: [cesanta/docker_auth:stable](https://registry.hub.docker.com/u/cesanta/docker_auth/).
+A public Docker image is available on Docker Hub: [cesanta/docker](https://registry.hub.docker.com/u/cesanta/docker_auth/).
+
+Tags available:
+ - `:latest` - bleeding edge, usually works but breaking config changes are possible. You probably do not want to use this in production.
+ - `:1` - the `1.x` version, will have fixes, no breaking config changes. Previously known as `:stable`.
+ - `:1.x` - specific release, see [here](https://github.com/cesanta/docker_auth/releases) for the list of current releases.
The binary takes a single argument - path to the config file.
If no arguments are given, the Dockerfile defaults to `/config/auth_config.yml`.
@@ -35,7 +41,7 @@ $ docker run \
--rm -it --name docker_auth -p 5001:5001 \
-v /path/to/config_dir:/config:ro \
-v /var/log/docker_auth:/logs \
- cesanta/docker_auth:stable /config/auth_config.yml
+ cesanta/docker_auth:1 /config/auth_config.yml
```
See the [example config files](https://github.com/cesanta/docker_auth/tree/master/examples/) to get an idea of what is possible.
@@ -44,7 +50,7 @@ See the [example config files](https://github.com/cesanta/docker_auth/tree/maste
Run with increased verbosity:
```{r, engine='bash', count_lines}
-docker run ... cesanta/docker_auth:stable --v=2 --alsologtostderr /config/auth_config.yml
+docker run ... cesanta/docker_auth:1 --v=2 --alsologtostderr /config/auth_config.yml
```
## Contributing
From 2cc197f58e4f28d1def55ce4824eea0f8675e323 Mon Sep 17 00:00:00 2001
From: Thatcher
Date: Mon, 30 Jan 2017 15:45:16 +0100
Subject: [PATCH 007/160] Fixed simple mistake in README (#164)
In text the link to the container was cesanta/docker, while it should (have) been cesanta/docker_auth
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f499c55f..85813f79 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ Supported authorization methods:
## Installation and Examples
-A public Docker image is available on Docker Hub: [cesanta/docker](https://registry.hub.docker.com/u/cesanta/docker_auth/).
+A public Docker image is available on Docker Hub: [cesanta/docker_auth](https://registry.hub.docker.com/u/cesanta/docker_auth/).
Tags available:
- `:latest` - bleeding edge, usually works but breaking config changes are possible. You probably do not want to use this in production.
From 286369f8bf1d79c27c9f92f2b38d93511f4a7fe6 Mon Sep 17 00:00:00 2001
From: Dennis de Greef
Date: Mon, 27 Feb 2017 06:39:58 +0100
Subject: [PATCH 008/160] Add ServerName to tlsConfig when InsecureSkipVerify
is false (#165)
---
auth_server/authn/ldap_auth.go | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go
index 38ec9dad..f8fc08ff 100644
--- a/auth_server/authn/ldap_auth.go
+++ b/auth_server/authn/ldap_auth.go
@@ -136,18 +136,25 @@ func (la *LDAPAuth) escapeAccountInput(account string) string {
func (la *LDAPAuth) ldapConnection() (*ldap.Conn, error) {
var l *ldap.Conn
var err error
+
+ tlsConfig := &tls.Config{InsecureSkipVerify: true}
+ if !la.config.InsecureTLSSkipVerify {
+ addr := strings.Split(la.config.Addr, ":")
+ tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0]}
+ }
+
if la.config.TLS == "" || la.config.TLS == "none" || la.config.TLS == "starttls" {
glog.V(2).Infof("Dial: starting...%s", la.config.Addr)
l, err = ldap.Dial("tcp", fmt.Sprintf("%s", la.config.Addr))
if err == nil && la.config.TLS == "starttls" {
glog.V(2).Infof("StartTLS...")
- if tlserr := l.StartTLS(&tls.Config{InsecureSkipVerify: la.config.InsecureTLSSkipVerify}); tlserr != nil {
+ if tlserr := l.StartTLS(tlsConfig); tlserr != nil {
return nil, tlserr
}
}
} else if la.config.TLS == "always" {
glog.V(2).Infof("DialTLS: starting...%s", la.config.Addr)
- l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s", la.config.Addr), &tls.Config{InsecureSkipVerify: la.config.InsecureTLSSkipVerify})
+ l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s", la.config.Addr), tlsConfig)
}
if err != nil {
return nil, err
From f298f05ef75b5e5f3f7f60f99c4e6baaf631538d Mon Sep 17 00:00:00 2001
From: rojer
Date: Mon, 3 Apr 2017 20:11:17 +0100
Subject: [PATCH 009/160] GitHub auth: Add GithubWebUri to the template context
Fixes #169
---
auth_server/authn/github_auth.go | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/auth_server/authn/github_auth.go b/auth_server/authn/github_auth.go
index 3a3de558..7e1a9445 100644
--- a/auth_server/authn/github_auth.go
+++ b/auth_server/authn/github_auth.go
@@ -76,7 +76,11 @@ func NewGitHubAuth(c *GitHubAuthConfig) (*GitHubAuth, error) {
}
func (gha *GitHubAuth) doGitHubAuthPage(rw http.ResponseWriter, req *http.Request) {
- if err := gha.tmpl.Execute(rw, struct{ ClientId string }{ClientId: gha.config.ClientId}); err != nil {
+ if err := gha.tmpl.Execute(rw, struct {
+ ClientId, GithubWebUri string
+ }{
+ ClientId: gha.config.ClientId,
+ GithubWebUri: gha.getGithubWebUri()}); err != nil {
http.Error(rw, fmt.Sprintf("Template error: %s", err), http.StatusInternalServerError)
}
}
@@ -114,7 +118,7 @@ func (gha *GitHubAuth) doGitHubAuthCreateToken(rw http.ResponseWriter, code stri
"client_id": []string{gha.config.ClientId},
"client_secret": []string{gha.config.ClientSecret},
}
-
+
req, err := http.NewRequest("POST", fmt.Sprintf("%s/login/oauth/access_token", gha.getGithubWebUri()), bytes.NewBufferString(data.Encode()))
if err != nil {
http.Error(rw, fmt.Sprintf("Error creating request to GitHub auth backend: %s", err), http.StatusServiceUnavailable)
From 80d8a93b116c319a705ac73ab408d9471b596fdf Mon Sep 17 00:00:00 2001
From: rojer
Date: Mon, 3 Apr 2017 20:25:30 +0100
Subject: [PATCH 010/160] Fix parsing of IPv6 RemoteAddr
Fixes #168
---
auth_server/server/server.go | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/auth_server/server/server.go b/auth_server/server/server.go
index 3ddd3ecc..6f4de2f2 100644
--- a/auth_server/server/server.go
+++ b/auth_server/server/server.go
@@ -23,6 +23,7 @@ import (
"math/rand"
"net"
"net/http"
+ "regexp"
"sort"
"strings"
"time"
@@ -33,6 +34,10 @@ import (
"github.com/golang/glog"
)
+var (
+ hostPortRegex = regexp.MustCompile(`\[?(.+?)\]?:\d+$`)
+)
+
type AuthServer struct {
config *Config
authenticators []authn.Authenticator
@@ -131,12 +136,9 @@ func (ar authRequest) String() string {
}
func parseRemoteAddr(ra string) net.IP {
- colonIndex := strings.LastIndex(ra, ":")
- if colonIndex > 0 && ra[colonIndex-1] >= 0x30 && ra[colonIndex-1] <= 0x39 {
- ra = ra[:colonIndex]
- }
- if ra[0] == '[' && ra[len(ra)-1] == ']' { // IPv6
- ra = ra[1 : len(ra)-1]
+ hp := hostPortRegex.FindStringSubmatch(ra)
+ if hp != nil {
+ ra = string(hp[1])
}
res := net.ParseIP(ra)
return res
From e37561bf234aeb2295bdec9a1307f2d1f6036167 Mon Sep 17 00:00:00 2001
From: rojer
Date: Mon, 3 Apr 2017 20:44:18 +0100
Subject: [PATCH 011/160] Send WWW-Authenticate header with 401 response
Not sure what difference it'll make, but shouldn't hurt.
Fixes #152
---
auth_server/server/server.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/auth_server/server/server.go b/auth_server/server/server.go
index 6f4de2f2..be5a2947 100644
--- a/auth_server/server/server.go
+++ b/auth_server/server/server.go
@@ -376,6 +376,7 @@ func (as *AuthServer) doAuth(rw http.ResponseWriter, req *http.Request) {
}
if !authnResult {
glog.Warningf("Auth failed: %s", *ar)
+ rw.Header()["WWW-Authenticate"] = []string{fmt.Sprintf(`Basic realm="%s"`, as.config.Token.Issuer)}
http.Error(rw, "Auth failed.", http.StatusUnauthorized)
return
}
From 917428ac38d7c034d4e1ed7e13f0fd650c82b368 Mon Sep 17 00:00:00 2001
From: rojer
Date: Tue, 4 Apr 2017 01:44:48 +0100
Subject: [PATCH 012/160] Revamp the build process to incorporate govendor
Use the official golang base image for release builds
Fixes #150
---
auth_server/.gitignore | 2 +-
auth_server/Makefile | 48 ++--
auth_server/vendor/vendor.json | 449 +++++++++++++++++++++++++++++++++
3 files changed, 478 insertions(+), 21 deletions(-)
create mode 100644 auth_server/vendor/vendor.json
diff --git a/auth_server/.gitignore b/auth_server/.gitignore
index ea8e804c..e63dae2a 100644
--- a/auth_server/.gitignore
+++ b/auth_server/.gitignore
@@ -1,4 +1,4 @@
ca-certificates.crt
auth_server
-Godeps/
+vendor/*/
version.*
diff --git a/auth_server/Makefile b/auth_server/Makefile
index f716b448..25369812 100644
--- a/auth_server/Makefile
+++ b/auth_server/Makefile
@@ -4,47 +4,55 @@ COMPRESS_BINARY ?= false
CA_BUNDLE = /etc/ssl/certs/ca-certificates.crt
VERSION = $(shell cat version.txt)
-BUILDER_IMAGE ?= centurylink/golang-builder
-BUILDER_IMAGE_EXTRA-build-cross = -cross
-BUILDER_OPTS-docker-build = -v /var/run/docker.sock:/var/run/docker.sock
-BUILDER_IMAGE_EXTRA-docker-build =
+BUILDER_IMAGE ?= golang:1.8.0-alpine
.PHONY: %
all: build
-local: build-local
-update-deps:
- go get -v -u -f github.com/tools/godep github.com/jteeuwen/go-bindata/...
- go generate ./...
-
-godep:
- godep save
+deps:
+ go get -v -u github.com/kardianos/govendor
+ govendor sync
+ go install -v github.com/cesanta/docker_auth/auth_server/vendor/github.com/jteeuwen/go-bindata/go-bindata
-build-local: update-deps
- go build
+build:
+ go generate ./...
+ go build -v -i
ca-certificates.crt:
cp $(CA_BUNDLE) .
-docker-build:
- go generate ./...
- docker run --rm -v $(PWD):/src -e COMPRESS_BINARY=$(COMPRESS_BINARY) $(BUILDER_OPTS-$@) $(BUILDER_IMAGE)$(BUILDER_IMAGE_EXTRA-$@) $(IMAGE)
+build-release: ca-certificates.crt
+ docker run --rm -v $(PWD)/..:/go/src/github.com/cesanta/docker_auth \
+ $(BUILDER_IMAGE) sh -x -c "\
+ apk update && apk add git py2-pip && pip install GitPython && \
+ cd /go/src/github.com/cesanta/docker_auth/auth_server && \
+ go get -v -u github.com/kardianos/govendor && \
+ umask 0 && govendor sync -v && \
+ go install -v github.com/cesanta/docker_auth/auth_server/vendor/github.com/jteeuwen/go-bindata/go-bindata && \
+ go generate ./... && \
+ go build -v"
@echo === Built version $(VERSION) ===
-build build-cross: update-deps godep ca-certificates.crt docker-build
+auth_server:
+ @echo
+ @echo Use build or build-release to produce the auth_server binary
+ @echo
+ @exit 1
-docker-tag:
+docker-build: auth_server
+ docker build -t $(IMAGE):latest .
docker tag $(IMAGE):latest $(IMAGE):$(VERSION)
docker-tag-%:
docker tag $(IMAGE):latest $(IMAGE):$*
docker-push:
+ docker push $(IMAGE):latest
docker push $(IMAGE):$(VERSION)
docker-push-%: docker-tag-%
docker push $(IMAGE):$*
-# Shortcut for latest
-docker-push: docker-push-latest
+clean:
+ rm -rf auth_server vendor/*/*
diff --git a/auth_server/vendor/vendor.json b/auth_server/vendor/vendor.json
new file mode 100644
index 00000000..e0f1ec28
--- /dev/null
+++ b/auth_server/vendor/vendor.json
@@ -0,0 +1,449 @@
+{
+ "comment": "",
+ "ignore": "",
+ "package": [
+ {
+ "checksumSHA1": "CujWu7+PWlZSX5+zAPJH91O5AVQ=",
+ "origin": "github.com/docker/distribution/vendor/github.com/Sirupsen/logrus",
+ "path": "github.com/Sirupsen/logrus",
+ "revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
+ "revisionTime": "2017-03-21T17:14:25Z"
+ },
+ {
+ "checksumSHA1": "drfK6NPoGhSk7ZD6uhL+JjEpKy4=",
+ "path": "github.com/dchest/uniuri",
+ "revision": "8902c56451e9b58ff940bbe5fec35d5f9c04584a",
+ "revisionTime": "2016-02-12T16:43:26Z"
+ },
+ {
+ "checksumSHA1": "slvmkxxMkd1KThsdCXp1cUH+/H0=",
+ "path": "github.com/deckarep/golang-set",
+ "revision": "fc8930a5e645572ee00bf66358ed3414f3c13b90",
+ "revisionTime": "2017-02-02T20:30:32Z"
+ },
+ {
+ "checksumSHA1": "rAbbq6Q42Svc1VuHABqk4Xin4vM=",
+ "path": "github.com/docker/distribution/context",
+ "revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
+ "revisionTime": "2017-03-21T17:14:25Z"
+ },
+ {
+ "checksumSHA1": "j9kYvq02nJOTQmEH3wUw2Z/ybd8=",
+ "path": "github.com/docker/distribution/registry/auth",
+ "revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
+ "revisionTime": "2017-03-21T17:14:25Z"
+ },
+ {
+ "checksumSHA1": "09n22FzloMhoNbsxOT46PHGZaJA=",
+ "path": "github.com/docker/distribution/registry/auth/token",
+ "revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
+ "revisionTime": "2017-03-21T17:14:25Z"
+ },
+ {
+ "checksumSHA1": "tNWbcdclT5h9ygMIsq3VG05S6B0=",
+ "path": "github.com/docker/distribution/uuid",
+ "revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
+ "revisionTime": "2017-03-21T17:14:25Z"
+ },
+ {
+ "checksumSHA1": "SEVXNIcbfW8UK108uGqG1Lk81zo=",
+ "path": "github.com/docker/libtrust",
+ "revision": "aabc10ec26b754e797f9028f4589c5b7bd90dc20",
+ "revisionTime": "2016-07-08T17:25:13Z"
+ },
+ {
+ "checksumSHA1": "ej5H4I19BGtX42Dfe1ozH1Gcd6k=",
+ "path": "github.com/docker/libtrust/testutil",
+ "revision": "aabc10ec26b754e797f9028f4589c5b7bd90dc20",
+ "revisionTime": "2016-07-08T17:25:13Z"
+ },
+ {
+ "checksumSHA1": "tih88XkJ8c/nGHdMsRoi10N64fQ=",
+ "path": "github.com/facebookgo/clock",
+ "revision": "600d898af40aa09a7a93ecb9265d87b0504b6f03",
+ "revisionTime": "2015-04-10T01:09:13Z"
+ },
+ {
+ "checksumSHA1": "+jzvj7Vad92fiskWGxOPWPHL0w0=",
+ "path": "github.com/facebookgo/httpdown",
+ "revision": "a3b1354551a26449fbe05f5d855937f6e7acbd71",
+ "revisionTime": "2016-03-23T22:10:27Z"
+ },
+ {
+ "checksumSHA1": "BMOlXPELdp72k01fl9wUEKawQ+U=",
+ "path": "github.com/facebookgo/stats",
+ "revision": "1b76add642e42c6ffba7211ad7b3939ce654526e",
+ "revisionTime": "2015-10-06T22:16:25Z"
+ },
+ {
+ "checksumSHA1": "RR3M5cSlp1ltM7oToZif2QzZz/c=",
+ "path": "github.com/go-ldap/ldap",
+ "revision": "13cedcf58a1ea124045dea529a66c849d3444c8e",
+ "revisionTime": "2017-03-05T04:08:57Z"
+ },
+ {
+ "checksumSHA1": "yUc84k7cfnRi9AlPFuRo77Y18Og=",
+ "path": "github.com/golang/glog",
+ "revision": "23def4e6c14b4da8ac2ed8007337bc5eb5007998",
+ "revisionTime": "2016-01-25T20:49:56Z"
+ },
+ {
+ "checksumSHA1": "IhK3rKOSR3UfWHe5JmYv7Fnmkrk=",
+ "path": "github.com/golang/snappy",
+ "revision": "553a641470496b2327abcac10b36396bd98e45c9",
+ "revisionTime": "2017-02-15T23:32:05Z"
+ },
+ {
+ "checksumSHA1": "Bev6wUWj9ao5HvSK9NkV4EahnS0=",
+ "origin": "github.com/docker/distribution/vendor/github.com/gorilla/context",
+ "path": "github.com/gorilla/context",
+ "revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
+ "revisionTime": "2017-03-21T17:14:25Z"
+ },
+ {
+ "checksumSHA1": "mfR6uobHATtDFPT+GU2BLm68Kdo=",
+ "origin": "github.com/docker/distribution/vendor/github.com/gorilla/mux",
+ "path": "github.com/gorilla/mux",
+ "revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
+ "revisionTime": "2017-03-21T17:14:25Z"
+ },
+ {
+ "checksumSHA1": "LAqhkCywKqkTvAu+MbIRnpeFnZA=",
+ "path": "github.com/jteeuwen/go-bindata",
+ "revision": "a0ff2567cfb70903282db057e799fd826784d41d",
+ "revisionTime": "2015-10-23T09:11:02Z"
+ },
+ {
+ "checksumSHA1": "1E4XSPmZqO/s20/WWJKXsyGTfm4=",
+ "path": "github.com/jteeuwen/go-bindata/go-bindata",
+ "revision": "a0ff2567cfb70903282db057e799fd826784d41d",
+ "revisionTime": "2015-10-23T09:11:02Z"
+ },
+ {
+ "checksumSHA1": "qafPKAxcGTNAL1S6Ly6hs4hVT8k=",
+ "path": "github.com/onsi/ginkgo",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "R0SNYpmISoxRcVWhRXLmWbOkqzY=",
+ "path": "github.com/onsi/ginkgo/config",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "Djfgj1lp/1lLdZvC6dfpe1xFWAU=",
+ "path": "github.com/onsi/ginkgo/internal/codelocation",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "bk3fPLYoCcaMCQCkr7l2F1k9qvE=",
+ "path": "github.com/onsi/ginkgo/internal/containernode",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "YXukUqtSZk/6dOoq02fRfp7HBtI=",
+ "path": "github.com/onsi/ginkgo/internal/failer",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "syt+qozqwaniN4cID+xdRyElZPc=",
+ "path": "github.com/onsi/ginkgo/internal/leafnodes",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "khPjshQRAjjziEG3ZY6Kwj1q0WA=",
+ "path": "github.com/onsi/ginkgo/internal/remote",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "biCzwu1LHcTDe9mVhb/CBvgKEl8=",
+ "path": "github.com/onsi/ginkgo/internal/spec",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "mbabEQNCg0JrWvAO/Sws2lICo3E=",
+ "path": "github.com/onsi/ginkgo/internal/specrunner",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "38Z+VxVz5LlFqoYGj2TO4u+BeFY=",
+ "path": "github.com/onsi/ginkgo/internal/suite",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "6sOxGhw1brknfjJd9h7MhpnxHMY=",
+ "path": "github.com/onsi/ginkgo/internal/testingtproxy",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "eGp6kNfhzh4z8EtiakksnO0+HWI=",
+ "path": "github.com/onsi/ginkgo/internal/writer",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "DrVQM/1iTuIyvayBTnuB6pWS51M=",
+ "path": "github.com/onsi/ginkgo/reporters",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "LFlv3sbzEoTcjS6gtctz/HLO74g=",
+ "path": "github.com/onsi/ginkgo/reporters/stenographer",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "a/OSgDX/WOpy+u4Lm1FVK57+w2k=",
+ "path": "github.com/onsi/ginkgo/types",
+ "revision": "25380c62e61d3f90436be125b8127dbae578fdef",
+ "revisionTime": "2015-06-27T18:45:31Z"
+ },
+ {
+ "checksumSHA1": "F2nzbTb5VserStm8/ZYLt7m6EMk=",
+ "path": "github.com/onsi/gomega",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "8v+lUNp0/5+3QZtUrJ7+DKFTL2U=",
+ "path": "github.com/onsi/gomega/format",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "ZzBlmFKcNKJ/rSy0Quv6d/gCZ3w=",
+ "path": "github.com/onsi/gomega/gbytes",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "H3kuVM+3XZZciTLxPOLh9YzhD+U=",
+ "path": "github.com/onsi/gomega/ghttp",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "wU1avNsF7pL+O8CV39/FoOUJZlY=",
+ "path": "github.com/onsi/gomega/internal/assertion",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "Yt5Wlj9S7Tu/ZClZOiOa5ombnQY=",
+ "path": "github.com/onsi/gomega/internal/asyncassertion",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "CIwteJCVKKQDPoXfqtVTVMHAeEU=",
+ "path": "github.com/onsi/gomega/internal/fakematcher",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "ufjDjg83v7dVAlpc5qjkH6ATInU=",
+ "path": "github.com/onsi/gomega/internal/testingtsupport",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "xOIZnI9Qj0TQhrMEI5QrosxK6ls=",
+ "path": "github.com/onsi/gomega/matchers",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "gWJfsDHiZTga1esEChd+MfpftN0=",
+ "path": "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "zjTC6ady0bJUwzTFAKtv63T7Fmg=",
+ "path": "github.com/onsi/gomega/matchers/support/goraph/edge",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "o2+IscLOPKOiovl2g0/igkD1R4Q=",
+ "path": "github.com/onsi/gomega/matchers/support/goraph/node",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "W1zfga0jmo7Daetjcur8v2hh0y8=",
+ "path": "github.com/onsi/gomega/matchers/support/goraph/util",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "zEuQ0PUlbqojtNwvu01Mn3DQCJM=",
+ "path": "github.com/onsi/gomega/types",
+ "revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
+ "revisionTime": "2015-05-30T21:13:11Z"
+ },
+ {
+ "checksumSHA1": "GVY3lzvj4xmpKOGgA4/h9GWjQVk=",
+ "path": "github.com/syndtr/goleveldb/leveldb",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "qITi3AleZHNDuMD7tEeP2NMOEwk=",
+ "path": "github.com/syndtr/goleveldb/leveldb/cache",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "5KPgnvCPlR0ysDAqo6jApzRQ3tw=",
+ "path": "github.com/syndtr/goleveldb/leveldb/comparer",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "1DRAxdlWzS4U0xKN/yQ/fdNN7f0=",
+ "path": "github.com/syndtr/goleveldb/leveldb/errors",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "8ssfBXjxEDGEmP9zZy+131Zpfig=",
+ "path": "github.com/syndtr/goleveldb/leveldb/filter",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "P5o3zyHp2reDi4OSnNGbXQcoT7s=",
+ "path": "github.com/syndtr/goleveldb/leveldb/iterator",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "3uB/sw+/s5EAXI/CTDx29Fed0kw=",
+ "path": "github.com/syndtr/goleveldb/leveldb/journal",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "kCsyA+AaHsJ+TEyefgxRjSW+w/0=",
+ "path": "github.com/syndtr/goleveldb/leveldb/memdb",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "UmQeotV+m8/FduKEfLOhjdp18rs=",
+ "path": "github.com/syndtr/goleveldb/leveldb/opt",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "XO6uR3Wew1G4uyHN8I9Gvl7twKY=",
+ "path": "github.com/syndtr/goleveldb/leveldb/storage",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "ne6LKzh6wCQQ05mnQ6kDyGHRyvs=",
+ "path": "github.com/syndtr/goleveldb/leveldb/table",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "eyYwOkjZtIdWyde94zaTS4RS22M=",
+ "path": "github.com/syndtr/goleveldb/leveldb/testutil",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "LZb6+6ryx3zxm5VxgQcIJyfdu6E=",
+ "path": "github.com/syndtr/goleveldb/leveldb/util",
+ "revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
+ "revisionTime": "2017-03-02T03:19:10Z"
+ },
+ {
+ "checksumSHA1": "5HymxKSV8zcw6eiNV5Z/MZBPmuU=",
+ "path": "golang.org/x/crypto/bcrypt",
+ "revision": "573951cbe80bb6352881271bb276f48749eab6f4",
+ "revisionTime": "2017-03-30T09:09:28Z"
+ },
+ {
+ "checksumSHA1": "nkSU+D5ZACy+jm3aEB+Sr5t0Eqc=",
+ "path": "golang.org/x/crypto/blowfish",
+ "revision": "573951cbe80bb6352881271bb276f48749eab6f4",
+ "revisionTime": "2017-03-30T09:09:28Z"
+ },
+ {
+ "checksumSHA1": "9QKY4bQlbQ/VZZFP8/1WtH4b0mA=",
+ "origin": "github.com/docker/distribution/vendor/golang.org/x/net/context",
+ "path": "golang.org/x/net/context",
+ "revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
+ "revisionTime": "2017-03-21T17:14:25Z"
+ },
+ {
+ "checksumSHA1": "AK65RmsGNBl0/e11OVrf2mW78gU=",
+ "path": "golang.org/x/sys/unix",
+ "revision": "493114f68206f85e7e333beccfabc11e98cba8dd",
+ "revisionTime": "2017-03-31T21:25:38Z"
+ },
+ {
+ "checksumSHA1": "fRERF7JFq7KYgM9I48onMlEgFm4=",
+ "path": "gopkg.in/asn1-ber.v1",
+ "revision": "4e86f4367175e39f69d9358a5f17b4dda270378d",
+ "revisionTime": "2015-09-24T05:17:56Z"
+ },
+ {
+ "checksumSHA1": "nGXjr6oY5leYbIOJNkJmHGiBp38=",
+ "path": "gopkg.in/fsnotify.v1",
+ "revision": "629574ca2a5df945712d3079857300b5e4da0236",
+ "revisionTime": "2016-10-11T02:33:12Z"
+ },
+ {
+ "checksumSHA1": "tefd2MHMp6qRXeE3jqzp3P40mNI=",
+ "path": "gopkg.in/mgo.v2",
+ "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
+ "revisionTime": "2016-08-18T02:01:20Z"
+ },
+ {
+ "checksumSHA1": "+mZKlPX9t4fHOQvkQeCnWw5JjkQ=",
+ "path": "gopkg.in/mgo.v2/bson",
+ "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
+ "revisionTime": "2016-08-18T02:01:20Z"
+ },
+ {
+ "checksumSHA1": "trGhCcEZSOZTwlLfWCRAoXXihW8=",
+ "path": "gopkg.in/mgo.v2/internal/json",
+ "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
+ "revisionTime": "2016-08-18T02:01:20Z"
+ },
+ {
+ "checksumSHA1": "LEvMCnprte47qdAxWvQ/zRxVF1U=",
+ "path": "gopkg.in/mgo.v2/internal/sasl",
+ "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
+ "revisionTime": "2016-08-18T02:01:20Z"
+ },
+ {
+ "checksumSHA1": "fOxeCpKRYNTTM2VCrsU2S/KEd1s=",
+ "path": "gopkg.in/mgo.v2/internal/scram",
+ "revision": "3f83fa5005286a7fe593b055f0d7771a7dce4655",
+ "revisionTime": "2016-08-18T02:01:20Z"
+ },
+ {
+ "checksumSHA1": "/0kOHaD3bhhN1GjmZajSigSqu4E=",
+ "path": "gopkg.in/yaml.v2",
+ "revision": "a3f3340b5840cee44f372bddb5880fcbc419b46a",
+ "revisionTime": "2017-02-08T14:18:51Z"
+ }
+ ],
+ "rootPath": "github.com/cesanta/docker_auth/auth_server"
+}
From 4f7154faefefc7010e33ad7a18f14489e9fab2ac Mon Sep 17 00:00:00 2001
From: Roman Vynar
Date: Wed, 5 Apr 2017 17:27:04 +0300
Subject: [PATCH 013/160] Fix checking of service field in ACL. (#170)
---
auth_server/authz/acl.go | 6 ++++--
auth_server/authz/acl_test.go | 11 +++++++++--
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/auth_server/authz/acl.go b/auth_server/authz/acl.go
index e58e7fd2..96e55b78 100644
--- a/auth_server/authz/acl.go
+++ b/auth_server/authz/acl.go
@@ -27,6 +27,7 @@ type MatchConditions struct {
Type *string `yaml:"type,omitempty" json:"type,omitempty"`
Name *string `yaml:"name,omitempty" json:"name,omitempty"`
IP *string `yaml:"ip,omitempty" json:"ip,omitempty"`
+ Service *string `yaml:"service,omitempty" json:"service,omitempty"`
Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"`
}
@@ -64,7 +65,7 @@ func parseIPPattern(ipp string) (*net.IPNet, error) {
}
func validateMatchConditions(mc *MatchConditions) error {
- for _, p := range []*string{mc.Account, mc.Type, mc.Name} {
+ for _, p := range []*string{mc.Account, mc.Type, mc.Name, mc.Service} {
if p == nil {
continue
}
@@ -201,7 +202,7 @@ func (mc *MatchConditions) Matches(ai *AuthRequestInfo) bool {
"${name}", regexp.QuoteMeta(ai.Name),
"${service}", regexp.QuoteMeta(ai.Service),
}
- for _, x := range []string{"Account", "Type", "Name"} {
+ for _, x := range []string{"Account", "Type", "Name", "Service"} {
field, _ := getField(mc, x)
for _, found := range captureGroupRegex.FindAllStringSubmatch(field, -1) {
key := strings.Title(found[1])
@@ -235,6 +236,7 @@ func (mc *MatchConditions) Matches(ai *AuthRequestInfo) bool {
return matchString(mc.Account, ai.Account, vars) &&
matchString(mc.Type, ai.Type, vars) &&
matchString(mc.Name, ai.Name, vars) &&
+ matchString(mc.Service, ai.Service, vars) &&
matchIP(mc.IP, ai.IP) &&
matchLabels(mc.Labels, ai.Labels, vars)
}
diff --git a/auth_server/authz/acl_test.go b/auth_server/authz/acl_test.go
index 51bb484d..4e6b2477 100644
--- a/auth_server/authz/acl_test.go
+++ b/auth_server/authz/acl_test.go
@@ -25,6 +25,9 @@ func TestValidation(t *testing.T) {
{MatchConditions{Name: sp("foo")}, true},
{MatchConditions{Name: sp("foo?*")}, true},
{MatchConditions{Name: sp("/foo.*/")}, true},
+ {MatchConditions{Service: sp("foo")}, true},
+ {MatchConditions{Service: sp("foo?*")}, true},
+ {MatchConditions{Service: sp("/foo.*/")}, true},
{MatchConditions{IP: sp("192.168.0.1")}, true},
{MatchConditions{IP: sp("192.168.0.0/16")}, true},
{MatchConditions{IP: sp("2001:db8::1")}, true},
@@ -34,6 +37,7 @@ func TestValidation(t *testing.T) {
{MatchConditions{Account: sp("/foo?*/")}, false},
{MatchConditions{Type: sp("/foo?*/")}, false},
{MatchConditions{Name: sp("/foo?*/")}, false},
+ {MatchConditions{Service: sp("/foo?*/")}, false},
{MatchConditions{IP: sp("192.168.0.1/100")}, false},
{MatchConditions{IP: sp("192.168.0.*")}, false},
{MatchConditions{IP: sp("foo")}, false},
@@ -51,8 +55,8 @@ func TestValidation(t *testing.T) {
}
func TestMatching(t *testing.T) {
- ai1 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "baz"}
- ai2 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "baz",
+ ai1 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "baz", Service: "notary"}
+ ai2 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "baz", Service: "notary",
Labels: map[string][]string{"group": []string{"admins", "VIP"}}}
cases := []struct {
mc MatchConditions
@@ -71,6 +75,9 @@ func TestMatching(t *testing.T) {
{MatchConditions{Account: sp(`/^(.+)@test\.com$/`), Name: sp(`${account:1}/*`)}, AuthRequestInfo{Account: "john.smith@test.com", Name: "john.smith/test"}, true},
{MatchConditions{Account: sp(`/^(.+)@test\.com$/`), Name: sp(`${account:3}/*`)}, AuthRequestInfo{Account: "john.smith@test.com", Name: "john.smith/test"}, false},
{MatchConditions{Account: sp(`/^(.+)@(.+?).test\.com$/`), Name: sp(`${account:1}-${account:2}/*`)}, AuthRequestInfo{Account: "john.smith@it.test.com", Name: "john.smith-it/test"}, true},
+ {MatchConditions{Service: sp("notary"), Type: sp("bar")}, ai1, true},
+ {MatchConditions{Service: sp("notary"), Type: sp("baz")}, ai1, false},
+ {MatchConditions{Service: sp("notary1"), Type: sp("bar")}, ai1, false},
// IP matching
{MatchConditions{IP: sp("127.0.0.1")}, AuthRequestInfo{IP: nil}, false},
{MatchConditions{IP: sp("127.0.0.1")}, AuthRequestInfo{IP: net.IPv4(127, 0, 0, 1)}, true},
From a53bceffb610b3a7f1ab10f332bf48048e75a8d4 Mon Sep 17 00:00:00 2001
From: rojer
Date: Wed, 19 Apr 2017 13:57:29 +0100
Subject: [PATCH 014/160] Fix auth page content type
Fixes #172
---
auth_server/server/server.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/auth_server/server/server.go b/auth_server/server/server.go
index be5a2947..78919862 100644
--- a/auth_server/server/server.go
+++ b/auth_server/server/server.go
@@ -349,7 +349,7 @@ func (as *AuthServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// https://developers.google.com/identity/sign-in/web/server-side-flow
func (as *AuthServer) doIndex(rw http.ResponseWriter, req *http.Request) {
- rw.Header().Set("Content-Type", "text-html; charset=utf-8")
+ rw.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(rw, "%s
\n", as.config.Token.Issuer)
if as.ga != nil {
fmt.Fprint(rw, `Login with Google account
`)
From 721c841bb9297e9ae0025ce5a77486380d7125c4 Mon Sep 17 00:00:00 2001
From: rojer
Date: Wed, 19 Apr 2017 17:38:26 +0100
Subject: [PATCH 015/160] Build a static binary
Fixes #173
---
auth_server/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/auth_server/Makefile b/auth_server/Makefile
index 25369812..120100dc 100644
--- a/auth_server/Makefile
+++ b/auth_server/Makefile
@@ -17,7 +17,7 @@ deps:
build:
go generate ./...
- go build -v -i
+ CGO_ENABLED=0 go build -v -i --ldflags=--s
ca-certificates.crt:
cp $(CA_BUNDLE) .
From d76a69c31cdef1ea1c21b0c675aaeaef6d87594f Mon Sep 17 00:00:00 2001
From: rojer
Date: Wed, 19 Apr 2017 19:43:40 +0100
Subject: [PATCH 016/160] Use cesanta/glog instead of golang/glog
For --logbufsecs
---
auth_server/authn/ext_auth.go | 2 +-
auth_server/authn/github_auth.go | 2 +-
auth_server/authn/google_auth.go | 2 +-
auth_server/authn/ldap_auth.go | 2 +-
auth_server/authn/mongo_auth.go | 2 +-
auth_server/authn/tokendb.go | 2 +-
auth_server/authz/acl.go | 2 +-
auth_server/authz/acl_mongo.go | 2 +-
auth_server/authz/ext_authz.go | 2 +-
auth_server/main.go | 2 +-
auth_server/mgo_session/mgo_session.go | 2 +-
auth_server/server/server.go | 2 +-
auth_server/vendor/vendor.json | 12 ++++++------
13 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/auth_server/authn/ext_auth.go b/auth_server/authn/ext_auth.go
index 9a1709ef..c26b660e 100644
--- a/auth_server/authn/ext_auth.go
+++ b/auth_server/authn/ext_auth.go
@@ -23,7 +23,7 @@ import (
"strings"
"syscall"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
)
type ExtAuthConfig struct {
diff --git a/auth_server/authn/github_auth.go b/auth_server/authn/github_auth.go
index 7e1a9445..0ce9c5ce 100644
--- a/auth_server/authn/github_auth.go
+++ b/auth_server/authn/github_auth.go
@@ -28,7 +28,7 @@ import (
"strings"
"time"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
)
type GitHubAuthConfig struct {
diff --git a/auth_server/authn/google_auth.go b/auth_server/authn/google_auth.go
index 93f1a94a..10891ab8 100644
--- a/auth_server/authn/google_auth.go
+++ b/auth_server/authn/google_auth.go
@@ -27,7 +27,7 @@ import (
"strings"
"time"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
)
type GoogleAuthConfig struct {
diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go
index f8fc08ff..3bdf7c39 100644
--- a/auth_server/authn/ldap_auth.go
+++ b/auth_server/authn/ldap_auth.go
@@ -24,7 +24,7 @@ import (
"strings"
"github.com/go-ldap/ldap"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
)
type LDAPAuthConfig struct {
diff --git a/auth_server/authn/mongo_auth.go b/auth_server/authn/mongo_auth.go
index 165fee40..72d08689 100644
--- a/auth_server/authn/mongo_auth.go
+++ b/auth_server/authn/mongo_auth.go
@@ -23,7 +23,7 @@ import (
"time"
"github.com/cesanta/docker_auth/auth_server/mgo_session"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
"golang.org/x/crypto/bcrypt"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
diff --git a/auth_server/authn/tokendb.go b/auth_server/authn/tokendb.go
index daaec171..a7d8bf0a 100644
--- a/auth_server/authn/tokendb.go
+++ b/auth_server/authn/tokendb.go
@@ -25,7 +25,7 @@ import (
"golang.org/x/crypto/bcrypt"
"github.com/dchest/uniuri"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
"github.com/syndtr/goleveldb/leveldb"
)
diff --git a/auth_server/authz/acl.go b/auth_server/authz/acl.go
index 96e55b78..19d36a91 100644
--- a/auth_server/authz/acl.go
+++ b/auth_server/authz/acl.go
@@ -11,7 +11,7 @@ import (
"strings"
"github.com/cesanta/docker_auth/auth_server/authn"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
)
type ACL []ACLEntry
diff --git a/auth_server/authz/acl_mongo.go b/auth_server/authz/acl_mongo.go
index b9194662..a5441103 100644
--- a/auth_server/authz/acl_mongo.go
+++ b/auth_server/authz/acl_mongo.go
@@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"github.com/cesanta/docker_auth/auth_server/mgo_session"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"io"
diff --git a/auth_server/authz/ext_authz.go b/auth_server/authz/ext_authz.go
index fdf00316..98890214 100644
--- a/auth_server/authz/ext_authz.go
+++ b/auth_server/authz/ext_authz.go
@@ -23,7 +23,7 @@ import (
"strings"
"syscall"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
)
type ExtAuthzConfig struct {
diff --git a/auth_server/main.go b/auth_server/main.go
index ad70282f..43b0d169 100644
--- a/auth_server/main.go
+++ b/auth_server/main.go
@@ -30,7 +30,7 @@ import (
"github.com/cesanta/docker_auth/auth_server/server"
"github.com/facebookgo/httpdown"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
fsnotify "gopkg.in/fsnotify.v1"
)
diff --git a/auth_server/mgo_session/mgo_session.go b/auth_server/mgo_session/mgo_session.go
index 5d1d2ba2..2f3dd93e 100644
--- a/auth_server/mgo_session/mgo_session.go
+++ b/auth_server/mgo_session/mgo_session.go
@@ -22,7 +22,7 @@ import (
"strings"
"time"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
"gopkg.in/mgo.v2"
)
diff --git a/auth_server/server/server.go b/auth_server/server/server.go
index 78919862..5d4f6571 100644
--- a/auth_server/server/server.go
+++ b/auth_server/server/server.go
@@ -31,7 +31,7 @@ import (
"github.com/cesanta/docker_auth/auth_server/authn"
"github.com/cesanta/docker_auth/auth_server/authz"
"github.com/docker/distribution/registry/auth/token"
- "github.com/golang/glog"
+ "github.com/cesanta/glog"
)
var (
diff --git a/auth_server/vendor/vendor.json b/auth_server/vendor/vendor.json
index e0f1ec28..50c9b8ae 100644
--- a/auth_server/vendor/vendor.json
+++ b/auth_server/vendor/vendor.json
@@ -9,6 +9,12 @@
"revision": "0700fa570d7bcc1b3e46ee127c4489fd25f4daa3",
"revisionTime": "2017-03-21T17:14:25Z"
},
+ {
+ "checksumSHA1": "60hRVfzu19loKucKk/vvBkuRfpg=",
+ "path": "github.com/cesanta/glog",
+ "revision": "22eb27a0ae192b290b25537b8e876556fc25129c",
+ "revisionTime": "2015-05-27T11:16:57Z"
+ },
{
"checksumSHA1": "drfK6NPoGhSk7ZD6uhL+JjEpKy4=",
"path": "github.com/dchest/uniuri",
@@ -81,12 +87,6 @@
"revision": "13cedcf58a1ea124045dea529a66c849d3444c8e",
"revisionTime": "2017-03-05T04:08:57Z"
},
- {
- "checksumSHA1": "yUc84k7cfnRi9AlPFuRo77Y18Og=",
- "path": "github.com/golang/glog",
- "revision": "23def4e6c14b4da8ac2ed8007337bc5eb5007998",
- "revisionTime": "2016-01-25T20:49:56Z"
- },
{
"checksumSHA1": "IhK3rKOSR3UfWHe5JmYv7Fnmkrk=",
"path": "github.com/golang/snappy",
From a76426387685c721962e84e29f60c85456b28e86 Mon Sep 17 00:00:00 2001
From: rojer
Date: Fri, 2 Jun 2017 14:32:37 +0100
Subject: [PATCH 017/160] Add support for LetsEncrypt
---
auth_server/Makefile | 2 +-
auth_server/main.go | 46 +++++++++++++++++++++-------------
auth_server/server/config.go | 31 +++++++++++++++++++----
auth_server/server/server.go | 2 +-
auth_server/vendor/vendor.json | 6 +++++
examples/reference.yml | 19 ++++++++++++--
6 files changed, 79 insertions(+), 27 deletions(-)
diff --git a/auth_server/Makefile b/auth_server/Makefile
index 120100dc..9812918e 100644
--- a/auth_server/Makefile
+++ b/auth_server/Makefile
@@ -40,7 +40,7 @@ auth_server:
@echo
@exit 1
-docker-build: auth_server
+docker-build: build
docker build -t $(IMAGE):latest .
docker tag $(IMAGE):latest $(IMAGE):$(VERSION)
diff --git a/auth_server/main.go b/auth_server/main.go
index 43b0d169..9694917d 100644
--- a/auth_server/main.go
+++ b/auth_server/main.go
@@ -29,8 +29,9 @@ import (
"time"
"github.com/cesanta/docker_auth/auth_server/server"
- "github.com/facebookgo/httpdown"
"github.com/cesanta/glog"
+ "github.com/facebookgo/httpdown"
+ "golang.org/x/crypto/acme/autocert"
fsnotify "gopkg.in/fsnotify.v1"
)
@@ -48,34 +49,43 @@ func ServeOnce(c *server.Config, cf string, hd *httpdown.HTTP) (*server.AuthServ
glog.Exitf("Failed to create auth server: %s", err)
}
- var tlsConfig *tls.Config
+ tlsConfig := &tls.Config{
+ MinVersion: tls.VersionTLS10,
+ PreferServerCipherSuites: true,
+ CipherSuites: []uint16{
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ tls.TLS_RSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+ },
+ NextProtos: []string{"http/1.1"},
+ }
if c.Server.CertFile != "" || c.Server.KeyFile != "" {
// Check for partial configuration.
if c.Server.CertFile == "" || c.Server.KeyFile == "" {
glog.Exitf("Failed to load certificate and key: both were not provided")
}
- tlsConfig = &tls.Config{
- MinVersion: tls.VersionTLS10,
- PreferServerCipherSuites: true,
- CipherSuites: []uint16{
- tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
- tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
- tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
- tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
- tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
- tls.TLS_RSA_WITH_AES_128_CBC_SHA,
- tls.TLS_RSA_WITH_AES_256_CBC_SHA,
- },
- NextProtos: []string{"http/1.1"},
- Certificates: make([]tls.Certificate, 1),
- }
glog.Infof("Cert file: %s", c.Server.CertFile)
glog.Infof("Key file : %s", c.Server.KeyFile)
+ tlsConfig.Certificates = make([]tls.Certificate, 1)
tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(c.Server.CertFile, c.Server.KeyFile)
if err != nil {
glog.Exitf("Failed to load certificate and key: %s", err)
}
+ } else if c.Server.LetsEncrypt.Email != "" {
+ m := &autocert.Manager{
+ Email: c.Server.LetsEncrypt.Email,
+ Prompt: autocert.AcceptTOS,
+ }
+ if c.Server.LetsEncrypt.Host != "" {
+ m.HostPolicy = autocert.HostWhitelist(c.Server.LetsEncrypt.Host)
+ }
+ glog.Infof("Using LetsEncrypt, host %q, email %q", c.Server.LetsEncrypt.Host, c.Server.LetsEncrypt.Email)
+ tlsConfig.GetCertificate = m.GetCertificate
} else {
glog.Warning("Running without TLS")
}
diff --git a/auth_server/server/config.go b/auth_server/server/config.go
index f6e53092..391fa9f7 100644
--- a/auth_server/server/config.go
+++ b/auth_server/server/config.go
@@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"io/ioutil"
+ "os"
"strings"
"time"
@@ -46,16 +47,23 @@ type Config struct {
}
type ServerConfig struct {
- ListenAddress string `yaml:"addr,omitempty"`
- RealIPHeader string `yaml:"real_ip_header,omitempty"`
- RealIPPos int `yaml:"real_ip_pos,omitempty"`
- CertFile string `yaml:"certificate,omitempty"`
- KeyFile string `yaml:"key,omitempty"`
+ ListenAddress string `yaml:"addr,omitempty"`
+ RealIPHeader string `yaml:"real_ip_header,omitempty"`
+ RealIPPos int `yaml:"real_ip_pos,omitempty"`
+ CertFile string `yaml:"certificate,omitempty"`
+ KeyFile string `yaml:"key,omitempty"`
+ LetsEncrypt LetsEncryptConfig `yaml:"letsencrypt,omitempty"`
publicKey libtrust.PublicKey
privateKey libtrust.PrivateKey
}
+type LetsEncryptConfig struct {
+ Host string `yaml:"host,omitempty"`
+ Email string `yaml:"email,omitempty"`
+ CacheDir string `yaml:"cache_dir,omitempty"`
+}
+
type TokenConfig struct {
Issuer string `yaml:"issuer,omitempty"`
CertFile string `yaml:"certificate,omitempty"`
@@ -208,5 +216,18 @@ func LoadConfig(fileName string) (*Config, error) {
if !tokenConfigured {
return nil, fmt.Errorf("failed to load token cert and key: none provided")
}
+
+ if !serverConfigured && c.Server.LetsEncrypt.Email != "" {
+ if c.Server.LetsEncrypt.CacheDir == "" {
+ return nil, fmt.Errorf("server.letsencrypt.cache_dir is required")
+ }
+ // We require that LetsEncrypt is an existing directory, because we really don't want it
+ // to be misconfigured and obtained certificates to be lost.
+ fi, err := os.Stat(c.Server.LetsEncrypt.CacheDir)
+ if err != nil || !fi.IsDir() {
+ return nil, fmt.Errorf("server.letsencrypt.cache_dir (%s) does not exist or is not a directory", c.Server.LetsEncrypt.CacheDir)
+ }
+ }
+
return c, nil
}
diff --git a/auth_server/server/server.go b/auth_server/server/server.go
index 5d4f6571..5f0fa24c 100644
--- a/auth_server/server/server.go
+++ b/auth_server/server/server.go
@@ -30,8 +30,8 @@ import (
"github.com/cesanta/docker_auth/auth_server/authn"
"github.com/cesanta/docker_auth/auth_server/authz"
- "github.com/docker/distribution/registry/auth/token"
"github.com/cesanta/glog"
+ "github.com/docker/distribution/registry/auth/token"
)
var (
diff --git a/auth_server/vendor/vendor.json b/auth_server/vendor/vendor.json
index 50c9b8ae..f261b9f2 100644
--- a/auth_server/vendor/vendor.json
+++ b/auth_server/vendor/vendor.json
@@ -371,6 +371,12 @@
"revision": "3c5717caf1475fd25964109a0fc640bd150fce43",
"revisionTime": "2017-03-02T03:19:10Z"
},
+ {
+ "checksumSHA1": "EUHyLhfP6B9rbXf8avkDXn5FTqE=",
+ "path": "golang.org/x/crypto/acme",
+ "revision": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e",
+ "revisionTime": "2017-05-23T23:42:09Z"
+ },
{
"checksumSHA1": "5HymxKSV8zcw6eiNV5Z/MZBPmuU=",
"path": "golang.org/x/crypto/bcrypt",
diff --git a/examples/reference.yml b/examples/reference.yml
index 716a8d01..2cf4957a 100644
--- a/examples/reference.yml
+++ b/examples/reference.yml
@@ -12,10 +12,25 @@
server: # Server settings.
# Address to listen on.
addr: ":5001"
- # TLS certificate and key.
- # If not specified, server will open a plain HTTP listener. In that case token.certificate and key must be provided.
+
+ # TLS options.
+ #
+ # Use specific certificate and key.
certificate: "/path/to/server.pem"
key: "/path/to/server.key"
+ # Use LetsEncrypt (https://letsencrypt.org/) to automatically obtain and maintain a certificate.
+ # Note that this only applies to server TLS certificate, this certificate will not be used for tokens
+ letsencrypt:
+ # Email is required. It will be used to register with LetsEncrypt.
+ email: webmaster@example.org
+ # Cache directory, where certificates issued by LE will be stored. Must exist.
+ # It is recommended to make it a volume mount so it persists across restarts.
+ cache_dir: /data/sslcache
+ # Normally LetsEncrypt will obtain a certificate for whichever host the client is connecting to.
+ # With this option, you can limit it to a specific host name.
+ # host: "docker.example.org"
+ # If neither certificate+key or letsencrypt are configured, the listener does not use TLS.
+
# Take client's address from the specified HTTP header instead of connection.
# May be useful if the server is behind a proxy or load balancer.
# If configured, this header must be present, requests without it will be rejected.
From d2bca957d854f9609682c8ebcea7a500e944ba01 Mon Sep 17 00:00:00 2001
From: rojer
Date: Tue, 6 Jun 2017 23:40:05 +0100
Subject: [PATCH 018/160] Fix non-TLS mode
Fixes #180
---
auth_server/main.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/auth_server/main.go b/auth_server/main.go
index 9694917d..2337e7ba 100644
--- a/auth_server/main.go
+++ b/auth_server/main.go
@@ -88,6 +88,7 @@ func ServeOnce(c *server.Config, cf string, hd *httpdown.HTTP) (*server.AuthServ
tlsConfig.GetCertificate = m.GetCertificate
} else {
glog.Warning("Running without TLS")
+ tlsConfig = nil
}
hs := &http.Server{
Addr: c.Server.ListenAddress,
From 6a3bc8c52bcd5f12cf870dbc3b9e2e138ceae9bc Mon Sep 17 00:00:00 2001
From: Alon Bar-Lev
Date: Tue, 11 Jul 2017 10:05:33 +0300
Subject: [PATCH 019/160] Feature: URL Prefix support (#181) (#185)
Usable when service is behind a proxy.
Tested with docker-distribution and apache httpd.
---
auth_server/server/config.go | 4 ++++
auth_server/server/server.go | 9 +++++----
examples/reference.yml | 3 +++
3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/auth_server/server/config.go b/auth_server/server/config.go
index 391fa9f7..427b9c14 100644
--- a/auth_server/server/config.go
+++ b/auth_server/server/config.go
@@ -48,6 +48,7 @@ type Config struct {
type ServerConfig struct {
ListenAddress string `yaml:"addr,omitempty"`
+ PathPrefix string `yaml:"path_prefix,omitempty"`
RealIPHeader string `yaml:"real_ip_header,omitempty"`
RealIPPos int `yaml:"real_ip_pos,omitempty"`
CertFile string `yaml:"certificate,omitempty"`
@@ -78,6 +79,9 @@ func validate(c *Config) error {
if c.Server.ListenAddress == "" {
return errors.New("server.addr is required")
}
+ if c.Server.PathPrefix != "" && !strings.HasPrefix(c.Server.PathPrefix, "/") {
+ return errors.New("server.path_prefix must be an absolute path")
+ }
if c.Token.Issuer == "" {
return errors.New("token.issuer is required")
diff --git a/auth_server/server/server.go b/auth_server/server/server.go
index 5f0fa24c..75dffba9 100644
--- a/auth_server/server/server.go
+++ b/auth_server/server/server.go
@@ -332,14 +332,15 @@ func (as *AuthServer) CreateToken(ar *authRequest, ares []authzResult) (string,
func (as *AuthServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
glog.V(3).Infof("Request: %+v", req)
+ path_prefix := as.config.Server.PathPrefix
switch {
- case req.URL.Path == "/":
+ case req.URL.Path == path_prefix + "/":
as.doIndex(rw, req)
- case req.URL.Path == "/auth":
+ case req.URL.Path == path_prefix + "/auth":
as.doAuth(rw, req)
- case req.URL.Path == "/google_auth" && as.ga != nil:
+ case req.URL.Path == path_prefix + "/google_auth" && as.ga != nil:
as.ga.DoGoogleAuth(rw, req)
- case req.URL.Path == "/github_auth" && as.gha != nil:
+ case req.URL.Path == path_prefix + "/github_auth" && as.gha != nil:
as.gha.DoGitHubAuth(rw, req)
default:
http.Error(rw, "Not found", http.StatusNotFound)
diff --git a/examples/reference.yml b/examples/reference.yml
index 2cf4957a..839f8c68 100644
--- a/examples/reference.yml
+++ b/examples/reference.yml
@@ -13,6 +13,9 @@ server: # Server settings.
# Address to listen on.
addr: ":5001"
+ # URL path prefix to use.
+ path_prefix: ""
+
# TLS options.
#
# Use specific certificate and key.
From 87d796033c70c8a3366db2235ac7b29cff752ce3 Mon Sep 17 00:00:00 2001
From: Mark Ide
Date: Tue, 18 Jul 2017 15:07:04 -0400
Subject: [PATCH 020/160] Adds a note regarding Third-Party Cookies
When using docker_auth with Google or GitHub (OAuth)
authentication, the browser will not be able to complete
the request if third-party cookies are blocked
---
examples/reference.yml | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/examples/reference.yml b/examples/reference.yml
index 839f8c68..0271f8bb 100644
--- a/examples/reference.yml
+++ b/examples/reference.yml
@@ -73,7 +73,8 @@ google_auth:
domain: "example.com" # Optional. If set, only logins from this domain are accepted.
# client_id and client_secret for API access. Required.
# Follow instructions here: https://developers.google.com/identity/sign-in/web/devconsole-project
- # NB: Make sure JavaScript origins are configured correctly.
+ # NB: Make sure JavaScript origins are configured correctly, and that third-party
+ # cookies are not blocked in the browser being used to login.
client_id: "1223123456-somethingsomething.apps.googleusercontent.com"
# Either client_secret or client_secret_file is required. Use client_secret_file if you don't
# want to have sensitive information checked in.
@@ -93,7 +94,8 @@ github_auth:
organization: "acme" # Optional. If set, only logins from this organization are accepted.
# client_id and client_secret for API access. Required.
# You can register a new application here: https://github.com/settings/developers
- # NB: Make sure JavaScript origins are configured correctly.
+ # NB: Make sure JavaScript origins are configured correctly, and that third-party
+ # cookies are not blocked in the browser being used to login.
client_id: "1223123456"
# Either client_secret or client_secret_file is required. Use client_secret_file if you don't
# want to have sensitive information checked in.
From dcf6adb8a2015c56b3a693da47994287095bc908 Mon Sep 17 00:00:00 2001
From: Miroslav Genov
Date: Mon, 7 Aug 2017 14:31:48 +0300
Subject: [PATCH 021/160] docker_auth/github: pass read:org scope for the
authorization (#190)
The read:org scope is required for the authorization as it allows user
to check whether it's a member of the organization or not.
GitHub Scopes for OAuth Apps are described:
https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-scopes-for-oauth-apps/
Fixes #189
---
auth_server/authn/data/github_auth.tmpl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/auth_server/authn/data/github_auth.tmpl b/auth_server/authn/data/github_auth.tmpl
index 05daa798..67529e99 100644
--- a/auth_server/authn/data/github_auth.tmpl
+++ b/auth_server/authn/data/github_auth.tmpl
@@ -1,6 +1,6 @@
-
+
From 7a39a9fd713bdc77cbc03349501dd8060c7b17a2 Mon Sep 17 00:00:00 2001
From: Miroslav Genov
Date: Mon, 7 Aug 2017 19:27:10 +0300
Subject: [PATCH 022/160] doc: document label usage in reference.yml
Added label usage examples in the reference.yml.
Fixes #188
---
examples/reference.yml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/examples/reference.yml b/examples/reference.yml
index 0271f8bb..b8bb08ca 100644
--- a/examples/reference.yml
+++ b/examples/reference.yml
@@ -229,6 +229,12 @@ acl:
- match: {account: "/^(.+)@test.com$/", name: "${account:1}/*"}
actions: []
comment: "Emit domain part of account to make it a correct repo name"
+ - match: {labels: {"group": "VIP"}}
+ actions: ["push"]
+ comment: "Users assigned to group 'VIP' is able to push"
+ - match: {labels: {"group": "/trainee|dev/"}}
+ actions: ["push", "pull"]
+ comment: "Users assigned to group 'trainee' and 'dev' is able to push and pull"
# Access is denied by default.
# (optional) Define to query ACL from a MongoDB server.
From a213b075c474b33eae8ccf93c5edd9765561dec7 Mon Sep 17 00:00:00 2001
From: Carson Anderson
Date: Tue, 29 Aug 2017 14:15:32 -0600
Subject: [PATCH 023/160] Return labels from mongo users
---
auth_server/authn/mongo_auth.go | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/auth_server/authn/mongo_auth.go b/auth_server/authn/mongo_auth.go
index 72d08689..c6aee896 100644
--- a/auth_server/authn/mongo_auth.go
+++ b/auth_server/authn/mongo_auth.go
@@ -43,6 +43,7 @@ type MongoAuth struct {
type authUserEntry struct {
Username *string `yaml:"username,omitempty" json:"username,omitempty"`
Password *string `yaml:"password,omitempty" json:"password,omitempty"`
+ Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"`
}
func NewMongoAuth(c *MongoAuthConfig) (*MongoAuth, error) {
@@ -84,19 +85,19 @@ func NewMongoAuth(c *MongoAuthConfig) (*MongoAuth, error) {
func (mauth *MongoAuth) Authenticate(account string, password PasswordString) (bool, Labels, error) {
for true {
- result, err := mauth.authenticate(account, password)
+ result, labels, err := mauth.authenticate(account, password)
if err == io.EOF {
glog.Warningf("EOF error received from Mongo. Retrying connection")
time.Sleep(time.Second)
continue
}
- return result, nil, err
+ return result, labels, err
}
return false, nil, errors.New("Unable to communicate with Mongo.")
}
-func (mauth *MongoAuth) authenticate(account string, password PasswordString) (bool, error) {
+func (mauth *MongoAuth) authenticate(account string, password PasswordString) (bool, Labels, error) {
// Copy our session
tmp_session := mauth.session.Copy()
// Close up when we are done
@@ -111,20 +112,20 @@ func (mauth *MongoAuth) authenticate(account string, password PasswordString) (b
// If we connect and get no results we return a NoMatch so auth can fall-through
if err == mgo.ErrNotFound {
- return false, NoMatch
+ return false, nil, NoMatch
} else if err != nil {
- return false, err
+ return false, nil, err
}
// Validate db password against passed password
if dbUserRecord.Password != nil {
if bcrypt.CompareHashAndPassword([]byte(*dbUserRecord.Password), []byte(password)) != nil {
- return false, nil
+ return false, nil, nil
}
}
// Auth success
- return true, nil
+ return true, dbUserRecord.Labels, nil
}
// Validate ensures that any custom config options
From 4e49efe8986425eb52f5494d94d29b512dfa5bc3 Mon Sep 17 00:00:00 2001
From: Miroslav Genov
Date: Tue, 15 Aug 2017 20:21:12 +0300
Subject: [PATCH 024/160] docker_auth/github: store tokens in google cloud
storage
Added a new implementation of TokenDB that uses Google Cloud Storage as
backend for storing of the tokens.
This makes container independent from the file system of the container
or from the target system and is good alternative to mounted clustered
file systems to container.
Docs examples are updated regarding this change as specifying token_db
configuration now becomes alternate between local file and gcs.
---
auth_server/authn/github_auth.go | 37 +++++++---
auth_server/authn/tokendb.go | 4 +-
auth_server/authn/tokendb_gcs.go | 121 +++++++++++++++++++++++++++++++
auth_server/server/config.go | 8 +-
auth_server/vendor/vendor.json | 14 +++-
examples/reference.yml | 6 +-
6 files changed, 173 insertions(+), 17 deletions(-)
create mode 100644 auth_server/authn/tokendb_gcs.go
diff --git a/auth_server/authn/github_auth.go b/auth_server/authn/github_auth.go
index 0ce9c5ce..6a47aa53 100644
--- a/auth_server/authn/github_auth.go
+++ b/auth_server/authn/github_auth.go
@@ -32,15 +32,21 @@ import (
)
type GitHubAuthConfig struct {
- Organization string `yaml:"organization,omitempty"`
- ClientId string `yaml:"client_id,omitempty"`
- ClientSecret string `yaml:"client_secret,omitempty"`
- ClientSecretFile string `yaml:"client_secret_file,omitempty"`
- TokenDB string `yaml:"token_db,omitempty"`
- HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
- RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"`
- GithubWebUri string `yaml:"github_web_uri,omitempty"`
- GithubApiUri string `yaml:"github_api_uri,omitempty"`
+ Organization string `yaml:"organization,omitempty"`
+ ClientId string `yaml:"client_id,omitempty"`
+ ClientSecret string `yaml:"client_secret,omitempty"`
+ ClientSecretFile string `yaml:"client_secret_file,omitempty"`
+ TokenDB string `yaml:"token_db,omitempty"`
+ GCSTokenDB *GitHubGCSStoreConfig `yaml:"gcs_token_db,omitempty"`
+ HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
+ RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"`
+ GithubWebUri string `yaml:"github_web_uri,omitempty"`
+ GithubApiUri string `yaml:"github_api_uri,omitempty"`
+}
+
+type GitHubGCSStoreConfig struct {
+ Bucket string `yaml:"bucket,omitempty"`
+ ClientSecretFile string `yaml:"client_secret_file,omitempty"`
}
type GitHubAuthRequest struct {
@@ -62,11 +68,20 @@ type GitHubAuth struct {
}
func NewGitHubAuth(c *GitHubAuthConfig) (*GitHubAuth, error) {
- db, err := NewTokenDB(c.TokenDB)
+ var db TokenDB
+ var err error
+ dbName := c.TokenDB
+ if c.GCSTokenDB == nil {
+ db, err = NewTokenDB(c.TokenDB)
+ } else {
+ db, err = NewGCSTokenDB(c.GCSTokenDB.Bucket, c.GCSTokenDB.ClientSecretFile)
+ dbName = "GCS: " + c.GCSTokenDB.Bucket
+ }
+
if err != nil {
return nil, err
}
- glog.Infof("GitHub auth token DB at %s", c.TokenDB)
+ glog.Infof("GitHub auth token DB at %s", dbName)
return &GitHubAuth{
config: c,
db: db,
diff --git a/auth_server/authn/tokendb.go b/auth_server/authn/tokendb.go
index a7d8bf0a..6f650c57 100644
--- a/auth_server/authn/tokendb.go
+++ b/auth_server/authn/tokendb.go
@@ -24,8 +24,8 @@ import (
"golang.org/x/crypto/bcrypt"
- "github.com/dchest/uniuri"
"github.com/cesanta/glog"
+ "github.com/dchest/uniuri"
"github.com/syndtr/goleveldb/leveldb"
)
@@ -93,7 +93,7 @@ func (db *TokenDBImpl) GetValue(user string) (*TokenDBValue, error) {
err = json.Unmarshal(valueStr, &dbv)
if err != nil {
glog.Errorf("bad DB value for %q (%q): %s", user, string(valueStr), err)
- return nil, fmt.Errorf("bad DB value", err)
+ return nil, fmt.Errorf("bad DB value due: %v", err)
}
return &dbv, nil
}
diff --git a/auth_server/authn/tokendb_gcs.go b/auth_server/authn/tokendb_gcs.go
new file mode 100644
index 00000000..68158ff1
--- /dev/null
+++ b/auth_server/authn/tokendb_gcs.go
@@ -0,0 +1,121 @@
+/*
+ Copyright 2017 Cesanta Software Ltd.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+package authn
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "cloud.google.com/go/storage"
+ "github.com/cesanta/glog"
+ "github.com/dchest/uniuri"
+ "golang.org/x/crypto/bcrypt"
+ "golang.org/x/net/context"
+ "google.golang.org/api/option"
+)
+
+// NewGCSTokenDB return a new TokenDB structure which uses Google Cloud Storage as backend. The
+// created DB uses file-per-user strategy and stores credentials independently for each user.
+//
+// Note: it's not recomanded bucket to be shared with other apps or services
+func NewGCSTokenDB(bucket, clientSecretFile string) (TokenDB, error) {
+ gcs, err := storage.NewClient(context.Background(), option.WithServiceAccountFile(clientSecretFile))
+ return &gcsTokenDB{gcs, bucket}, err
+}
+
+type gcsTokenDB struct {
+ gcs *storage.Client
+ bucket string
+}
+
+// GetValue gets token value associated with the provided user. Each user
+// in the bucket is having it's own file for tokens and it's recomanded bucket
+// to not be shared with other apps
+func (db *gcsTokenDB) GetValue(user string) (*TokenDBValue, error) {
+ rd, err := db.gcs.Bucket(db.bucket).Object(user).NewReader(context.Background())
+ if err == storage.ErrObjectNotExist {
+ return nil, nil
+ }
+ if err != nil {
+ return nil, fmt.Errorf("could not retrieved token for user '%s' due: %v", user, err)
+ }
+ defer rd.Close()
+
+ var dbv TokenDBValue
+ if err := json.NewDecoder(rd).Decode(&dbv); err != nil {
+ glog.Errorf("bad DB value for %q: %v", user, err)
+ return nil, fmt.Errorf("could not read token for user '%s' due: %v", user, err)
+ }
+
+ return &dbv, nil
+}
+
+// StoreToken stores token in the GCS file in a JSON format. Note that separate file is
+// used for each user
+func (db *gcsTokenDB) StoreToken(user string, v *TokenDBValue, updatePassword bool) (dp string, err error) {
+ if updatePassword {
+ dp = uniuri.New()
+ dph, _ := bcrypt.GenerateFromPassword([]byte(dp), bcrypt.DefaultCost)
+ v.DockerPassword = string(dph)
+ }
+
+ wr := db.gcs.Bucket(db.bucket).Object(user).NewWriter(context.Background())
+
+ if err := json.NewEncoder(wr).Encode(v); err != nil {
+ glog.Errorf("failed to set token data for %s: %s", user, err)
+ return "", fmt.Errorf("failed to set token data for %s due: %v", user, err)
+ }
+
+ err = wr.Close()
+ return
+}
+
+// ValidateToken verifies whether the provided token passed as password field
+// is still valid, e.g available and not expired
+func (db *gcsTokenDB) ValidateToken(user string, password PasswordString) error {
+ dbv, err := db.GetValue(user)
+ if err != nil {
+ return err
+ }
+ if dbv == nil {
+ return NoMatch
+ }
+
+ if bcrypt.CompareHashAndPassword([]byte(dbv.DockerPassword), []byte(password)) != nil {
+ return WrongPass
+ }
+ if time.Now().After(dbv.ValidUntil) {
+ return ExpiredToken
+ }
+
+ return nil
+}
+
+// DeleteToken deletes the GCS file that is associated with the provided user.
+func (db *gcsTokenDB) DeleteToken(user string) error {
+ ctx := context.Background()
+ err := db.gcs.Bucket(db.bucket).Object(user).Delete(ctx)
+ if err == storage.ErrObjectNotExist {
+ return nil
+ }
+ return err
+}
+
+// Close is a nop operation for this db
+func (db *gcsTokenDB) Close() error {
+ return nil
+}
diff --git a/auth_server/server/config.go b/auth_server/server/config.go
index 427b9c14..fe08c6d0 100644
--- a/auth_server/server/config.go
+++ b/auth_server/server/config.go
@@ -120,8 +120,12 @@ func validate(c *Config) error {
}
ghac.ClientSecret = strings.TrimSpace(string(contents))
}
- if ghac.ClientId == "" || ghac.ClientSecret == "" || ghac.TokenDB == "" {
- return errors.New("github_auth.{client_id,client_secret,token_db} are required.")
+ if ghac.ClientId == "" || ghac.ClientSecret == "" || (ghac.TokenDB == "" && ghac.GCSTokenDB == nil) {
+ return errors.New("github_auth.{client_id,client_secret,token_db} are required")
+ }
+
+ if ghac.ClientId == "" || ghac.ClientSecret == "" || (ghac.GCSTokenDB != nil && (ghac.GCSTokenDB.Bucket == "" || ghac.GCSTokenDB.ClientSecretFile == "")) {
+ return errors.New("github_auth.{client_id,client_secret,gcs_token_db{bucket,client_secret_file}} are required")
}
if ghac.HTTPTimeout <= 0 {
ghac.HTTPTimeout = time.Duration(10 * time.Second)
diff --git a/auth_server/vendor/vendor.json b/auth_server/vendor/vendor.json
index f261b9f2..9bfddc3c 100644
--- a/auth_server/vendor/vendor.json
+++ b/auth_server/vendor/vendor.json
@@ -2,6 +2,12 @@
"comment": "",
"ignore": "",
"package": [
+ {
+ "checksumSHA1": "0ot8Hk23WGrT0lE2BmQXeqe4bRo=",
+ "path": "cloud.google.com/go/storage",
+ "revision": "2b74e2e25316cfd9e46b74e444cdeceb78786dc5",
+ "revisionTime": "2017-08-20T12:51:33Z"
+ },
{
"checksumSHA1": "CujWu7+PWlZSX5+zAPJH91O5AVQ=",
"origin": "github.com/docker/distribution/vendor/github.com/Sirupsen/logrus",
@@ -397,11 +403,17 @@
"revisionTime": "2017-03-21T17:14:25Z"
},
{
- "checksumSHA1": "AK65RmsGNBl0/e11OVrf2mW78gU=",
+ "checksumSHA1": "1WoWjPiwUEFahi5xz29FRMtd8sA=",
"path": "golang.org/x/sys/unix",
"revision": "493114f68206f85e7e333beccfabc11e98cba8dd",
"revisionTime": "2017-03-31T21:25:38Z"
},
+ {
+ "checksumSHA1": "RpAaByicZuXzN7bReX8YXKf8gP0=",
+ "path": "google.golang.org/api/option",
+ "revision": "955a3ae66b420f3adc0d77da3d8ed767a74e2b4f",
+ "revisionTime": "2017-09-01T00:04:07Z"
+ },
{
"checksumSHA1": "fRERF7JFq7KYgM9I48onMlEgFm4=",
"path": "gopkg.in/asn1-ber.v1",
diff --git a/examples/reference.yml b/examples/reference.yml
index b8bb08ca..61638fdd 100644
--- a/examples/reference.yml
+++ b/examples/reference.yml
@@ -101,8 +101,12 @@ github_auth:
# want to have sensitive information checked in.
# client_secret: "verysecret"
client_secret_file: "/path/to/client_secret.txt"
- # Where to store server tokens. Required.
+ # Either token_db file for storing of server tokens.
token_db: "/somewhere/to/put/github_tokens.ldb"
+ # or google cloud storage for storing of the sensitive information.
+ gcs_token_db:
+ bucket: "tokenBucket"
+ client_secret_file: "/path/to/client_secret.json"
# How long to wait when talking to GitHub servers. Optional.
http_timeout: "10s"
# How long to wait before revalidating the GitHub token. Optional.
From aea8fdcdf3d126882c6f93ec7ce38273cfe4ad5b Mon Sep 17 00:00:00 2001
From: Carson Anderson
Date: Tue, 29 Aug 2017 16:34:05 -0600
Subject: [PATCH 025/160] Add matching of label placeholders
---
auth_server/authz/acl.go | 53 ++++++++++++++++++++--
auth_server/authz/acl_test.go | 16 +++++++
auth_server/vendor/vendor.json | 8 +++-
docs/Backend_MongoDB.md | 23 ++++++++--
docs/Labels.md | 81 ++++++++++++++++++++++++++++++++++
examples/reference.yml | 10 +++++
6 files changed, 182 insertions(+), 9 deletions(-)
create mode 100644 docs/Labels.md
diff --git a/auth_server/authz/acl.go b/auth_server/authz/acl.go
index 19d36a91..7404a72f 100644
--- a/auth_server/authz/acl.go
+++ b/auth_server/authz/acl.go
@@ -12,6 +12,7 @@ import (
"github.com/cesanta/docker_auth/auth_server/authn"
"github.com/cesanta/glog"
+ "github.com/schwarmco/go-cartesian-product"
)
type ACL []ACLEntry
@@ -153,6 +154,42 @@ func matchString(pp *string, s string, vars []string) bool {
return err == nil && matched
}
+func matchStringWithLabelPermutations(pp *string, s string, vars []string, labelMap *map[string][]string) bool {
+ var matched bool
+ // First try basic matching
+ matched = matchString(pp, s, vars)
+ // If basic matching fails then try with label permuations
+ if !matched {
+ // Take the labelMap and build the structure required for the cartesian library
+ var labelSets [][]interface{}
+ for placeholder, labels := range *labelMap {
+ // Don't bother generating perumations for placeholders not in match string
+ // Since the label permuations are a cartesian product this can have
+ // a huge impact on performance
+ if strings.Contains(*pp, placeholder) {
+ var labelSet []interface{}
+ for _, label := range labels {
+ labelSet = append(labelSet, []string{placeholder, label})
+ }
+ labelSets = append(labelSets, labelSet)
+ }
+ }
+ if len(labelSets) > 0 {
+ for permuation := range cartesian.Iter(labelSets...) {
+ var labelVars []string
+ for _, val := range permuation {
+ labelVars = append(labelVars, val.([]string)...)
+ }
+ matched = matchString(pp, s, append(vars, labelVars...))
+ if matched {
+ break
+ }
+ }
+ }
+ }
+ return matched
+}
+
func matchIP(ipp *string, ip net.IP) bool {
if ipp == nil {
return true
@@ -233,10 +270,18 @@ func (mc *MatchConditions) Matches(ai *AuthRequestInfo) bool {
vars = append(vars, found[0], text[index])
}
}
- return matchString(mc.Account, ai.Account, vars) &&
- matchString(mc.Type, ai.Type, vars) &&
- matchString(mc.Name, ai.Name, vars) &&
- matchString(mc.Service, ai.Service, vars) &&
+ labelMap := make(map[string][]string)
+ for label, labelValues := range ai.Labels {
+ var labelSet []string
+ for _, lv := range labelValues {
+ labelSet = append(labelSet, regexp.QuoteMeta(lv))
+ }
+ labelMap[fmt.Sprintf("${labels:%s}", label)] = labelSet
+ }
+ return matchStringWithLabelPermutations(mc.Account, ai.Account, vars, &labelMap) &&
+ matchStringWithLabelPermutations(mc.Type, ai.Type, vars, &labelMap) &&
+ matchStringWithLabelPermutations(mc.Name, ai.Name, vars, &labelMap) &&
+ matchStringWithLabelPermutations(mc.Service, ai.Service, vars, &labelMap) &&
matchIP(mc.IP, ai.IP) &&
matchLabels(mc.Labels, ai.Labels, vars)
}
diff --git a/auth_server/authz/acl_test.go b/auth_server/authz/acl_test.go
index 4e6b2477..3a8bd31d 100644
--- a/auth_server/authz/acl_test.go
+++ b/auth_server/authz/acl_test.go
@@ -58,6 +58,12 @@ func TestMatching(t *testing.T) {
ai1 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "baz", Service: "notary"}
ai2 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "baz", Service: "notary",
Labels: map[string][]string{"group": []string{"admins", "VIP"}}}
+ ai3 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "admins/foo", Service: "notary",
+ Labels: map[string][]string{"group": []string{"admins", "VIP"}}}
+ ai4 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "VIP/api", Service: "notary",
+ Labels: map[string][]string{"group": []string{"admins", "VIP"}, "project": []string{"api", "frontend"}}}
+ ai5 := AuthRequestInfo{Account: "foo", Type: "bar", Name: "devs/api", Service: "notary",
+ Labels: map[string][]string{"group": []string{"admins", "VIP"}, "project": []string{"api", "frontend"}}}
cases := []struct {
mc MatchConditions
ai AuthRequestInfo
@@ -99,6 +105,16 @@ func TestMatching(t *testing.T) {
{MatchConditions{Labels: map[string]string{"group": "VIP"}}, ai2, true},
{MatchConditions{Labels: map[string]string{"group": "a*"}}, ai2, true},
{MatchConditions{Labels: map[string]string{"group": "/(admins|VIP)/"}}, ai2, true},
+ // // Label placeholder matching
+ {MatchConditions{Name: sp("${labels:group}/*")}, ai1, false}, // no labels
+ {MatchConditions{Name: sp("${labels:noexist}/*")}, ai2, false}, // wrong labels
+ {MatchConditions{Name: sp("${labels:group}/*")}, ai3, true}, // match label
+ {MatchConditions{Name: sp("${labels:noexist}/*")}, ai3, false}, // missing label
+ {MatchConditions{Name: sp("${labels:group}/${labels:project}")}, ai4, true}, // multiple label match success
+ {MatchConditions{Name: sp("${labels:group}/${labels:noexist}")}, ai4, false}, // multiple label match fail
+ {MatchConditions{Name: sp("${labels:group}/${labels:project}")}, ai4, true}, // multiple label match success
+ {MatchConditions{Name: sp("${labels:group}/${labels:noexist}")}, ai4, false}, // multiple label match fail wrong label
+ {MatchConditions{Name: sp("${labels:group}/${labels:project}")}, ai5, false}, // multiple label match fail. right label, wrong value
}
for i, c := range cases {
if result := c.mc.Matches(&c.ai); result != c.matches {
diff --git a/auth_server/vendor/vendor.json b/auth_server/vendor/vendor.json
index f261b9f2..f63520b0 100644
--- a/auth_server/vendor/vendor.json
+++ b/auth_server/vendor/vendor.json
@@ -293,6 +293,12 @@
"revision": "d6c945f9fdbf6cad99e85b0feff591caa268e0db",
"revisionTime": "2015-05-30T21:13:11Z"
},
+ {
+ "checksumSHA1": "DVgRSBT6UzmEgC90yJhh6X5A7Yc=",
+ "path": "github.com/schwarmco/go-cartesian-product",
+ "revision": "c2c0aca869a6cbf51e017ce148b949d9dee09bc3",
+ "revisionTime": "2017-01-30T17:09:49Z"
+ },
{
"checksumSHA1": "GVY3lzvj4xmpKOGgA4/h9GWjQVk=",
"path": "github.com/syndtr/goleveldb/leveldb",
@@ -397,7 +403,7 @@
"revisionTime": "2017-03-21T17:14:25Z"
},
{
- "checksumSHA1": "AK65RmsGNBl0/e11OVrf2mW78gU=",
+ "checksumSHA1": "1WoWjPiwUEFahi5xz29FRMtd8sA=",
"path": "golang.org/x/sys/unix",
"revision": "493114f68206f85e7e333beccfabc11e98cba8dd",
"revisionTime": "2017-03-31T21:25:38Z"
diff --git a/docs/Backend_MongoDB.md b/docs/Backend_MongoDB.md
index 54a13e51..5a322ead 100644
--- a/docs/Backend_MongoDB.md
+++ b/docs/Backend_MongoDB.md
@@ -10,12 +10,21 @@ which can query ACL and Auth from a MongoDB database.
## Auth backend in MongoDB
Auth entries in mongo are single dictionary containing a username and password entry.
-The password entry must contain a BCrypt hash.
+The password entry must contain a BCrypt hash. The labels entry is optional.
```json
{
"username" : "admin",
- "password" : "$2y$05$B.x046DV3bvuwFgn0I42F.W/SbRU5fUoCbCGtjFl7S33aCUHNBxbq"
+ "password" : "$2y$05$B.x046DV3bvuwFgn0I42F.W/SbRU5fUoCbCGtjFl7S33aCUHNBxbq",
+ "labels" : {
+ "group" : [
+ "dev"
+ ],
+ "project": [
+ "website",
+ "api"
+ ]
+ }
}
```
@@ -43,15 +52,21 @@ guarantee by default, i.e. [Natural Sorting](https://docs.mongodb.org/manual/ref
``seq`` is a required field in all MongoDB ACL documents. Any documents without this key will be excluded. seq uniqeness is also enforced.
-**reference_acl.json**
+ - match: {labels: {"group": "/trainee|dev/"}}
+ actions: ["push", "pull"]
+ comment: "Users assigned to group 'trainee' and 'dev' is able to push and pull"
+**reference_acl.json**
```json
{"seq": 10, "match" : {"account" : "admin"}, "actions" : ["*"], "comment" : "Admin has full access to everything."}
+{"seq": 11, "match" : {"labels": {"group": "admin"}}, "actions" : ["*"], "comment" : "Admin group members have full access to everything"}
{"seq": 20, "match" : {"account" : "test", "name" : "test-*"}, "actions" : ["*"], "comment" : "User \"test\" has full access to test-* images but nothing else. (1)"}
{"seq": 30, "match" : {"account" : "test"}, "actions" : [], "comment" : "User \"test\" has full access to test-* images but nothing else. (2)"}
{"seq": 40, "match" : {"account" : "/.+/"}, "actions" : ["pull"], "comment" : "All logged in users can pull all images."}
{"seq": 50, "match" : {"account" : "/.+/", "name" : "${account}/*"}, "actions" : ["*"], "comment" : "All logged in users can push all images that are in a namespace beginning with their name"}
-{"seq": 60, "match" : {"account" : "", "name" : "hello-world"}, "actions" : ["pull"], "comment" : "Anonymous users can pull \"hello-world\"."}
+{"seq": 60, "match" : {"name" : "${labels:group}-shared/*"}, "actions" : ["push", "pull"], "comment" : "Users can pull and push to the shared namespace of any group they are in"}
+{"seq": 70, "match" : {"name" : "${labels:project}/*"}, "actions" : ["push", "pull"], "comment" : "Users can pull and push to to namespaces matching projects they are assigned to"}
+{"seq": 80, "match" : {"account" : "", "name" : "hello-world"}, "actions" : ["pull"], "comment" : "Anonymous users can pull \"hello-world\"."}
```
**Note** that each document entry must span exactly one line or otherwise the
diff --git a/docs/Labels.md b/docs/Labels.md
new file mode 100644
index 00000000..34b3a468
--- /dev/null
+++ b/docs/Labels.md
@@ -0,0 +1,81 @@
+# Labels
+
+Labels can be used to reduce the number ACLS needed in large, complex installations.
+
+## Label Placeholders
+
+Label placeholders are available for any label that is assigned to a user.
+
+For example, given a user:
+
+```json
+{
+ "username" : "busy-guy",
+ "password" : "$2y$05$B.x046DV3bvuwFgn0I42F.W/SbRU5fUoCbCGtjFl7S33aCUHNBxbq",
+ "labels" : {
+ "group" : [
+ "web",
+ "webdev"
+ ],
+ "project" : [
+ "website",
+ "api"
+ ],
+ "tier" : [
+ "frontend",
+ "backend"
+ ]
+ }
+}
+```
+
+The following placeholders could be used in any match field:
+
+ * `${labels:group}`
+ * `${labels:project}`
+ * `${labels:tier}`
+
+Example acl with label matching:
+
+```json
+{
+ "match": { "name": "${labels:project}/*" },
+ "actions": [ "push", "pull" ],
+ "comment": "Users can push to any project they are assigned to"
+}
+```
+
+Single label matching is efficient and will be tested in the order
+they are listed in the user record.
+
+
+## Using Multiple Labels when matching
+
+It's possible to use multiple labels in a single match. When multiple labels are
+used in a single match all possible combinations of the labels are tested
+in [no particular order](https://blog.golang.org/go-maps-in-action#TOC_7.).
+
+Example acl with multiple label matching:
+
+```json
+{
+ "match": { "name": "${labels:project}/${labels:group}-${labels:tier}" },
+ "actions": [ "push", "pull" ],
+ "comment": "Contrived multiple label match rule"
+}
+```
+
+When paired with the user given above would result in 8 possible combinations
+that would need to be tested.
+
+ * `${labels:project} : website`, `${labels:group} : dev`, `${labels:tier} : frontend`
+ * `${labels:project} : website`, `${labels:group} : dev`, `${labels:tier} : backend`
+ * `${labels:project} : website`, `${labels:group} : webdev`, `${labels:tier} : frontend`
+ * `${labels:project} : website`, `${labels:group} : webdev`, `${labels:tier} : backend`
+ * `${labels:project} : api`, `${labels:group} : dev`, `${labels:tier} : frontend`
+ * `${labels:project} : api`, `${labels:group} : dev`, `${labels:tier} : backend`
+ * `${labels:project} : api`, `${labels:group} : webdev`, `${labels:tier} : frontend`
+ * `${labels:project} : api`, `${labels:group} : webdev`, `${labels:tier} : backend`
+
+This grows rapidly as more placeholders and labels are added. So it's best
+to limit multiple label matching when possible.
diff --git a/examples/reference.yml b/examples/reference.yml
index b8bb08ca..54b83622 100644
--- a/examples/reference.yml
+++ b/examples/reference.yml
@@ -195,6 +195,7 @@ ext_auth:
# * ${service} - the service name, specified by auth.token.service in the registry config.
# * ${type} - the type of the entity, normally "repository".
# * ${name} - the name of the repository (i.e. image), e.g. centos.
+# * ${labels:
- $ docker login -u {{.Username}} -p {{.Password}} YOUR_REGISTRY_FQDN
+ $ docker login -u {{.Username}} -p {{.Password}} {{if .RegistryUrl}}{{.RegistryUrl}}{{else}}docker.example.com{{end}}
`)
@@ -195,7 +201,7 @@ func dataGithub_auth_resultTmpl() (*asset, error) {
return nil, err
}
- info := bindataFileInfo{name: "data/github_auth_result.tmpl", size: 1094, mode: os.FileMode(420), modTime: time.Unix(1, 0)}
+ info := bindataFileInfo{name: "data/github_auth_result.tmpl", size: 1300, mode: os.FileMode(420), modTime: time.Unix(1, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
diff --git a/auth_server/authn/data/github_auth_result.tmpl b/auth_server/authn/data/github_auth_result.tmpl
index df73761d..2619d0cd 100644
--- a/auth_server/authn/data/github_auth_result.tmpl
+++ b/auth_server/authn/data/github_auth_result.tmpl
@@ -32,6 +32,12 @@
border-radius: 0.5em;
text-shadow: 0px 1px 0px #fff;
}
+ .command span {
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
@@ -40,6 +46,6 @@
Use the following username and password to login into the registry:
- $ docker login -u {{.Username}} -p {{.Password}} YOUR_REGISTRY_FQDN
+ $ docker login -u {{.Username}} -p {{.Password}} {{if .RegistryUrl}}{{.RegistryUrl}}{{else}}docker.example.com{{end}}