Harbor 认证过程
Harbor以 Docker Registry v2认证为基础,添加上一层权限保护。
1.v2 集成了一个安全认证的功能,将安全认证暴露给外部服务,让外部服务去实现
2.强制用户每次Docker pull/push请求都要带一个合法的Token,Registry会通过公钥对Token进行解密验证
3.如果不包含Token,会被重定向到Token服务获取Token后重新请求
Registry v2 authentication官方文档
Registry v2 生成令牌代码
1.尝试使用registry开始推/拉操作
2.如果registry需要进行授权时,registry将会返回401 Unauthorized响应,同时在响应中包含了docker client如何进行认证的信息
3.registry client根据返回的信息,向auth server发送请求获取认证token
4.auth server将会根据查询的用户信息,生成token令牌,以及当前用户所具有的相关权限信息
5.client携带这附有token的请求,重试原始请求
6.registry接受了认证的token并且使得client继续操作
上述就是完整的授权过程.当用户完成上述过程以后便可以执行相关的pull/push操作。认证信息会每次都带在请求头中。
例:输入docker login,以下为整个登录过程流程图
1.registry 服务怎么知道服务认证地址?
registry 服务本身就提供了一个配置文件,可以在启动 registry 服务的配置文件中指定上认证服务地址即可
...
auth:
token:
realm: token-realm
service: token-service
issuer: registry-token-issuer
rootcertbundle: /root/certs/bundle
...
其中 realm 就可以用来指定一个认证服务的地址
Harbor 后台认证
- authserver
路径:src/core/service/token/token.go
func (h *Handler) Get() {
...
token, err := tokenCreator.Create(request)
...
}
路径:src/core/service/token/creator.go
func (g generalCreator) Create(r *http.Request) (*models.Token, error) {
...
access := GetResourceActions(scopes)
...
return MakeToken(r.Context(), ctx.GetUsername(), g.service, access)
}
路径:src/core/service/token/authutils.go
// MakeToken makes a valid jwt token based on parms.
func MakeToken(ctx context.Context, username, service string, access []*token.ResourceActions) (*models.Token, error) {
options, err := tokenpkg.NewOptions(signingMethod, v2.Issuer, privateKey)
if err != nil {
return nil, err
}
expiration, err := config.TokenExpiration(ctx)
if err != nil {
return nil, err
}
now := time.Now().UTC()
claims := &v2.Claims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: options.Issuer,
Subject: username,
Audience: jwt.ClaimStrings([]string{service}),
ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(expiration) * time.Minute)),
NotBefore: jwt.NewNumericDate(now),
IssuedAt: jwt.NewNumericDate(now),
ID: utils.GenerateRandomStringWithLen(16),
},
Access: access,
}
tok, err := tokenpkg.New(options, claims)
if err != nil {
return nil, err
}
// Add kid to token header for compatibility with docker distribution's code
// see https://github.com/docker/distribution/blob/release/2.7/registry/auth/token/token.go#L197
k, err := libtrust.UnmarshalPrivateKeyPEM(options.PrivateKey)
if err != nil {
return nil, err
}
tok.Header["kid"] = k.KeyID()
rawToken, err := tok.Raw()
if err != nil {
return nil, err
}
return &models.Token{
Token: rawToken,
ExpiresIn: expiration * 60,
IssuedAt: now.Format(time.RFC3339),
}, nil
}