Skip to content

Commit 5dd7b10

Browse files
authored
Merge branch 'cesanta:main' into mtls
2 parents 809a38d + 38e7252 commit 5dd7b10

File tree

21 files changed

+1076
-328
lines changed

21 files changed

+1076
-328
lines changed

.github/workflows/go_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ jobs:
44
test:
55
strategy:
66
matrix:
7-
go-version: [1.17.x, 1.18.x, 1.19.x, 1.20.x]
7+
go-version: [1.17.x, 1.18.x, 1.19.x, 1.20.x, 1.21.x]
88
os: [ubuntu-latest]
99
runs-on: ${{ matrix.os }}
1010
steps:

auth_server/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.20-alpine3.17 as build
1+
FROM golang:1.21-alpine3.19 as build
22

33
ARG VERSION
44
ENV VERSION "${VERSION}"
@@ -12,7 +12,7 @@ COPY . /build
1212
WORKDIR /build
1313
RUN make build
1414

15-
FROM alpine:3.17
15+
FROM alpine:3.19
1616
COPY --from=build /build/auth_server /docker_auth/
1717
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
1818
ENTRYPOINT ["/docker_auth/auth_server"]

auth_server/authn/data/github_auth_result.tmpl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@
4343
<body>
4444
<p class="message">
4545
You are successfully authenticated for the Docker Registry{{if .Organization}} with the <code>@{{.Organization}}</code> Github organization{{end}}.
46-
Use the following username and password to login into the registry:
46+
Log into the registry using one of these commands:
4747
</p>
4848
<hr>
4949
<pre class="command"><span>$ </span>docker login -u {{.Username}} -p {{.Password}} {{if .RegistryUrl}}{{.RegistryUrl}}{{else}}docker.example.com{{end}}</pre>
50+
<pre class="command"><span>$ </span>podman login -u {{.Username}} -p {{.Password}} {{if .RegistryUrl}}{{.RegistryUrl}}{{else}}docker.example.com{{end}}</pre>
51+
<pre class="command"><span>$ </span>nerdctl login -u {{.Username}} -p {{.Password}} {{if .RegistryUrl}}{{.RegistryUrl}}{{else}}docker.example.com{{end}}</pre>
5052
</body>
5153
</html>

auth_server/authn/github_auth.go

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"time"
3030

3131
"github.com/cesanta/glog"
32-
"github.com/go-redis/redis"
3332

3433
"github.com/cesanta/docker_auth/auth_server/api"
3534
)
@@ -57,28 +56,18 @@ type ParentGitHubTeam struct {
5756
}
5857

5958
type GitHubAuthConfig struct {
60-
Organization string `yaml:"organization,omitempty"`
61-
ClientId string `yaml:"client_id,omitempty"`
62-
ClientSecret string `yaml:"client_secret,omitempty"`
63-
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
64-
TokenDB string `yaml:"token_db,omitempty"`
65-
GCSTokenDB *GitHubGCSStoreConfig `yaml:"gcs_token_db,omitempty"`
66-
RedisTokenDB *GitHubRedisStoreConfig `yaml:"redis_token_db,omitempty"`
67-
HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
68-
RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"`
69-
GithubWebUri string `yaml:"github_web_uri,omitempty"`
70-
GithubApiUri string `yaml:"github_api_uri,omitempty"`
71-
RegistryUrl string `yaml:"registry_url,omitempty"`
72-
}
73-
74-
type GitHubGCSStoreConfig struct {
75-
Bucket string `yaml:"bucket,omitempty"`
76-
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
77-
}
78-
79-
type GitHubRedisStoreConfig struct {
80-
ClientOptions *redis.Options `yaml:"redis_options,omitempty"`
81-
ClusterOptions *redis.ClusterOptions `yaml:"redis_cluster_options,omitempty"`
59+
Organization string `yaml:"organization,omitempty"`
60+
ClientId string `yaml:"client_id,omitempty"`
61+
ClientSecret string `yaml:"client_secret,omitempty"`
62+
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
63+
LevelTokenDB *LevelDBStoreConfig `yaml:"level_token_db,omitempty"`
64+
GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"`
65+
RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"`
66+
HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
67+
RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"`
68+
GithubWebUri string `yaml:"github_web_uri,omitempty"`
69+
GithubApiUri string `yaml:"github_api_uri,omitempty"`
70+
RegistryUrl string `yaml:"registry_url,omitempty"`
8271
}
8372

8473
type GitHubAuthRequest struct {
@@ -169,17 +158,18 @@ func parseLinkHeader(linkLines []string) (linkHeader, error) {
169158
func NewGitHubAuth(c *GitHubAuthConfig) (*GitHubAuth, error) {
170159
var db TokenDB
171160
var err error
172-
dbName := c.TokenDB
161+
var dbName string
173162

174163
switch {
175164
case c.GCSTokenDB != nil:
176-
db, err = NewGCSTokenDB(c.GCSTokenDB.Bucket, c.GCSTokenDB.ClientSecretFile)
165+
db, err = NewGCSTokenDB(c.GCSTokenDB)
177166
dbName = "GCS: " + c.GCSTokenDB.Bucket
178167
case c.RedisTokenDB != nil:
179168
db, err = NewRedisTokenDB(c.RedisTokenDB)
180169
dbName = db.(*redisTokenDB).String()
181170
default:
182-
db, err = NewTokenDB(c.TokenDB)
171+
db, err = NewTokenDB(c.LevelTokenDB)
172+
dbName = c.LevelTokenDB.Path
183173
}
184174

185175
if err != nil {
@@ -191,7 +181,7 @@ func NewGitHubAuth(c *GitHubAuthConfig) (*GitHubAuth, error) {
191181
return &GitHubAuth{
192182
config: c,
193183
db: db,
194-
client: &http.Client{Timeout: 10 * time.Second},
184+
client: &http.Client{Timeout: c.HTTPTimeout},
195185
tmpl: template.Must(template.New("github_auth").Parse(string(github_auth))),
196186
tmplResult: template.Must(template.New("github_auth_result").Parse(string(github_auth_result))),
197187
}, nil

auth_server/authn/gitlab_auth.go

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"time"
3030

3131
"github.com/cesanta/glog"
32-
"github.com/go-redis/redis"
3332

3433
"github.com/cesanta/docker_auth/auth_server/api"
3534
)
@@ -57,20 +56,20 @@ type ParentGitlabTeam struct {
5756
}
5857

5958
type GitlabAuthConfig struct {
60-
Organization string `yaml:"organization,omitempty"`
61-
ClientId string `yaml:"client_id,omitempty"`
62-
ClientSecret string `yaml:"client_secret,omitempty"`
63-
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
64-
TokenDB string `yaml:"token_db,omitempty"`
65-
GCSTokenDB *GitlabGCSStoreConfig `yaml:"gcs_token_db,omitempty"`
66-
RedisTokenDB *GitlabRedisStoreConfig `yaml:"redis_token_db,omitempty"`
67-
HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
68-
RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"`
69-
GitlabWebUri string `yaml:"gitlab_web_uri,omitempty"`
70-
GitlabApiUri string `yaml:"gitlab_api_uri,omitempty"`
71-
RegistryUrl string `yaml:"registry_url,omitempty"`
72-
GrantType string `yaml:"grant_type,omitempty"`
73-
RedirectUri string `yaml:"redirect_uri,omitempty"`
59+
Organization string `yaml:"organization,omitempty"`
60+
ClientId string `yaml:"client_id,omitempty"`
61+
ClientSecret string `yaml:"client_secret,omitempty"`
62+
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
63+
LevelTokenDB *LevelDBStoreConfig `yaml:"level_token_db,omitempty"`
64+
GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"`
65+
RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"`
66+
HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
67+
RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"`
68+
GitlabWebUri string `yaml:"gitlab_web_uri,omitempty"`
69+
GitlabApiUri string `yaml:"gitlab_api_uri,omitempty"`
70+
RegistryUrl string `yaml:"registry_url,omitempty"`
71+
GrantType string `yaml:"grant_type,omitempty"`
72+
RedirectUri string `yaml:"redirect_uri,omitempty"`
7473
}
7574

7675
type CodeToGitlabTokenResponse struct {
@@ -85,16 +84,6 @@ type CodeToGitlabTokenResponse struct {
8584
ErrorDescription string `json:"error_description,omitempty"`
8685
}
8786

88-
type GitlabGCSStoreConfig struct {
89-
Bucket string `yaml:"bucket,omitempty"`
90-
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
91-
}
92-
93-
type GitlabRedisStoreConfig struct {
94-
ClientOptions *redis.Options `yaml:"redis_options,omitempty"`
95-
ClusterOptions *redis.ClusterOptions `yaml:"redis_cluster_options,omitempty"`
96-
}
97-
9887
type GitlabAuthRequest struct {
9988
Action string `json:"action,omitempty"`
10089
Code string `json:"code,omitempty"`
@@ -118,17 +107,18 @@ type GitlabAuth struct {
118107
func NewGitlabAuth(c *GitlabAuthConfig) (*GitlabAuth, error) {
119108
var db TokenDB
120109
var err error
121-
dbName := c.TokenDB
110+
var dbName string
122111

123112
switch {
124113
case c.GCSTokenDB != nil:
125-
db, err = NewGCSTokenDB(c.GCSTokenDB.Bucket, c.GCSTokenDB.ClientSecretFile)
114+
db, err = NewGCSTokenDB(c.GCSTokenDB)
126115
dbName = "GCS: " + c.GCSTokenDB.Bucket
127116
case c.RedisTokenDB != nil:
128-
db, err = NewRedisGitlabTokenDB(c.RedisTokenDB)
117+
db, err = NewRedisTokenDB(c.RedisTokenDB)
129118
dbName = db.(*redisTokenDB).String()
130119
default:
131-
db, err = NewTokenDB(c.TokenDB)
120+
db, err = NewTokenDB(c.LevelTokenDB)
121+
dbName = c.LevelTokenDB.Path
132122
}
133123

134124
if err != nil {
@@ -140,7 +130,7 @@ func NewGitlabAuth(c *GitlabAuthConfig) (*GitlabAuth, error) {
140130
return &GitlabAuth{
141131
config: c,
142132
db: db,
143-
client: &http.Client{Timeout: 10 * time.Second},
133+
client: &http.Client{Timeout: c.HTTPTimeout},
144134
tmpl: template.Must(template.New("gitlab_auth").Parse(string(gitlab_auth))),
145135
tmplResult: template.Must(template.New("gitlab_auth_result").Parse(string(gitlab_auth_result))),
146136
}, nil

auth_server/authn/google_auth.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ import (
3333
)
3434

3535
type GoogleAuthConfig struct {
36-
Domain string `yaml:"domain,omitempty"`
37-
ClientId string `yaml:"client_id,omitempty"`
38-
ClientSecret string `yaml:"client_secret,omitempty"`
39-
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
40-
TokenDB string `yaml:"token_db,omitempty"`
41-
HTTPTimeout int `yaml:"http_timeout,omitempty"`
36+
Domain string `yaml:"domain,omitempty"`
37+
ClientId string `yaml:"client_id,omitempty"`
38+
ClientSecret string `yaml:"client_secret,omitempty"`
39+
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
40+
LevelTokenDB *LevelDBStoreConfig `yaml:"level_token_db,omitempty"`
41+
GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"`
42+
RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"`
43+
HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
4244
}
4345

4446
type GoogleAuthRequest struct {
@@ -127,16 +129,30 @@ type GoogleAuth struct {
127129
}
128130

129131
func NewGoogleAuth(c *GoogleAuthConfig) (*GoogleAuth, error) {
130-
db, err := NewTokenDB(c.TokenDB)
132+
var db TokenDB
133+
var err error
134+
var dbName string
135+
136+
switch {
137+
case c.GCSTokenDB != nil:
138+
db, err = NewGCSTokenDB(c.GCSTokenDB)
139+
dbName = "GCS: " + c.GCSTokenDB.Bucket
140+
case c.RedisTokenDB != nil:
141+
db, err = NewRedisTokenDB(c.RedisTokenDB)
142+
dbName = db.(*redisTokenDB).String()
143+
default:
144+
db, err = NewTokenDB(c.LevelTokenDB)
145+
dbName = c.LevelTokenDB.Path
146+
}
131147
if err != nil {
132148
return nil, err
133149
}
134-
glog.Infof("Google auth token DB at %s", c.TokenDB)
150+
glog.Infof("Google auth token DB at %s", dbName)
135151
google_auth, _ := static.ReadFile("data/google_auth.tmpl")
136152
return &GoogleAuth{
137153
config: c,
138154
db: db,
139-
client: &http.Client{Timeout: 10 * time.Second},
155+
client: &http.Client{Timeout: c.HTTPTimeout},
140156
tmpl: template.Must(template.New("google_auth").Parse(string(google_auth))),
141157
}, nil
142158
}

auth_server/authn/oidc_auth.go

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,29 @@ import (
4040
type OIDCAuthConfig struct {
4141
// --- necessary ---
4242
// URL of the authentication provider. Must be able to serve the /.well-known/openid-configuration
43-
Issuer string `yaml:"issuer,omitempty"`
43+
Issuer string `yaml:"issuer,omitempty"`
4444
// URL of the auth server. Has to end with /oidc_auth
45-
RedirectURL string `yaml:"redirect_url,omitempty"`
45+
RedirectURL string `yaml:"redirect_url,omitempty"`
4646
// ID and secret, priovided by the OIDC provider after registration of the auth server
47-
ClientId string `yaml:"client_id,omitempty"`
48-
ClientSecret string `yaml:"client_secret,omitempty"`
49-
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
47+
ClientId string `yaml:"client_id,omitempty"`
48+
ClientSecret string `yaml:"client_secret,omitempty"`
49+
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
5050
// path where the tokendb should be stored within the container
51-
TokenDB string `yaml:"token_db,omitempty"`
51+
LevelTokenDB *LevelDBStoreConfig `yaml:"level_token_db,omitempty"`
52+
GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"`
53+
RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"`
5254
// --- optional ---
53-
HTTPTimeout int `yaml:"http_timeout,omitempty"`
55+
HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"`
5456
// the URL of the docker registry. Used to generate a full docker login command after authentication
55-
RegistryURL string `yaml:"registry_url,omitempty"`
57+
RegistryURL string `yaml:"registry_url,omitempty"`
5658
// --- optional ---
5759
// String claim to use for the username
58-
UserClaim string `yaml:"user_claim,omitempty"`
60+
UserClaim string `yaml:"user_claim,omitempty"`
5961
// --- optional ---
6062
// []string to add as labels.
61-
LabelsClaims []string `yaml:"labels_claims,omitempty"`
63+
LabelsClaims []string `yaml:"labels_claims,omitempty"`
6264
// --- optional ---
63-
Scopes []string `yaml:"scopes,omitempty"`
65+
Scopes []string `yaml:"scopes,omitempty"`
6466
}
6567

6668
// OIDCRefreshTokenResponse is sent by OIDC provider in response to the grant_type=refresh_token request.
@@ -92,11 +94,26 @@ type OIDCAuth struct {
9294
Creates everything necessary for OIDC auth.
9395
*/
9496
func NewOIDCAuth(c *OIDCAuthConfig) (*OIDCAuth, error) {
95-
db, err := NewTokenDB(c.TokenDB)
97+
var db TokenDB
98+
var err error
99+
var dbName string
100+
101+
switch {
102+
case c.GCSTokenDB != nil:
103+
db, err = NewGCSTokenDB(c.GCSTokenDB)
104+
dbName = "GCS: " + c.GCSTokenDB.Bucket
105+
case c.RedisTokenDB != nil:
106+
db, err = NewRedisTokenDB(c.RedisTokenDB)
107+
dbName = db.(*redisTokenDB).String()
108+
default:
109+
db, err = NewTokenDB(c.LevelTokenDB)
110+
dbName = c.LevelTokenDB.Path
111+
}
112+
96113
if err != nil {
97114
return nil, err
98115
}
99-
glog.Infof("OIDC auth token DB at %s", c.TokenDB)
116+
glog.Infof("OIDC auth token DB at %s", dbName)
100117
ctx := context.Background()
101118
oidcAuth, _ := static.ReadFile("data/oidc_auth.tmpl")
102119
oidcAuthResult, _ := static.ReadFile("data/oidc_auth_result.tmpl")
@@ -115,7 +132,7 @@ func NewOIDCAuth(c *OIDCAuthConfig) (*OIDCAuth, error) {
115132
return &OIDCAuth{
116133
config: c,
117134
db: db,
118-
client: &http.Client{Timeout: 10 * time.Second},
135+
client: &http.Client{Timeout: c.HTTPTimeout},
119136
tmpl: template.Must(template.New("oidc_auth").Parse(string(oidcAuth))),
120137
tmplResult: template.Must(template.New("oidc_auth_result").Parse(string(oidcAuthResult))),
121138
ctx: ctx,

auth_server/authn/tokendb_gcs.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,29 @@ import (
3131
"github.com/cesanta/docker_auth/auth_server/api"
3232
)
3333

34+
type GCSStoreConfig struct {
35+
Bucket string `yaml:"bucket,omitempty"`
36+
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
37+
TokenHashCost int `yaml:"token_hash_cost,omitempty"`
38+
}
39+
3440
// NewGCSTokenDB return a new TokenDB structure which uses Google Cloud Storage as backend. The
3541
// created DB uses file-per-user strategy and stores credentials independently for each user.
3642
//
3743
// Note: it's not recomanded bucket to be shared with other apps or services
38-
func NewGCSTokenDB(bucket, clientSecretFile string) (TokenDB, error) {
39-
gcs, err := storage.NewClient(context.Background(), option.WithServiceAccountFile(clientSecretFile))
40-
return &gcsTokenDB{gcs, bucket}, err
44+
func NewGCSTokenDB(options *GCSStoreConfig) (TokenDB, error) {
45+
gcs, err := storage.NewClient(context.Background(), option.WithServiceAccountFile(options.ClientSecretFile))
46+
tokenHashCost := options.TokenHashCost
47+
if tokenHashCost <= 0 {
48+
tokenHashCost = bcrypt.DefaultCost
49+
}
50+
return &gcsTokenDB{gcs, options.Bucket, tokenHashCost}, err
4151
}
4252

4353
type gcsTokenDB struct {
4454
gcs *storage.Client
4555
bucket string
56+
tokenHashCost int
4657
}
4758

4859
// GetValue gets token value associated with the provided user. Each user
@@ -72,7 +83,7 @@ func (db *gcsTokenDB) GetValue(user string) (*TokenDBValue, error) {
7283
func (db *gcsTokenDB) StoreToken(user string, v *TokenDBValue, updatePassword bool) (dp string, err error) {
7384
if updatePassword {
7485
dp = uniuri.New()
75-
dph, _ := bcrypt.GenerateFromPassword([]byte(dp), bcrypt.DefaultCost)
86+
dph, _ := bcrypt.GenerateFromPassword([]byte(dp), db.tokenHashCost)
7687
v.DockerPassword = string(dph)
7788
}
7889

0 commit comments

Comments
 (0)