一番瞎操作,Kubesphere 也能通过gitlab登录了

959 阅读3分钟

  最近接到了一个需求,技术团队迫切的希望开发过程中用到的林林总总的开源系统能够实现统一登录,免去频繁切换账户或忘记密码之苦。"恩。。就你了",看着Kubesphere挺顺眼,就先拿你动刀吧。

  翻看kubesphere的官网文档,果然不负所望,kubesphere3.1是支持oauth2登录功能的,并且已经内置了github和阿里云IDaas的插件,只需在ks-apiserver的配置文件中启用相应的oauth配置,即可实现;但是如果需要gitlab登录,就需要开发相应的插件了,这样的话,似乎就违背了低成本方便快捷实现的初衷了

  转念一想,既然gitlab和github"同宗同姓",那么github的配置是不是也可同样应用在gitlab上?于是乎投机取巧的使用github的配置代替,几番折腾下来,最终还是以失败收场,看来只能老老实实的开发插件了

 下载kubesphere源码,认证插件目录位置 /pkg/apiserver/authentication/identityprovider/   

接口定义


     kubesphere官方已对OAuth2认证逻辑进行了抽象和封装,按照官方指引,provider只需要实现几个约定的接口即可

image.png

接口实现


   kubesphere官方本身已经封装好了oauth验证逻辑,所以对于站在巨人肩上的开发者而言,只需要重新定义一个结构体gitlabIdentity,用以反序列化gitlab user api的用户信息

package gitlab

import (
    "context"
    "crypto/tls"
    "encoding/json"
    "io/ioutil"
    "net/http"
    "time"

    "github.com/mitchellh/mapstructure"
    "golang.org/x/oauth2"

    "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
    "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
)

// 初始化过程中,注册oauth Provider
func init() {
    identityprovider.RegisterOAuthProvider(&gitlabProviderFactory{})
}

type gitlab struct {
    // ClientID is the application's ID.
    ClientID string `json:"clientID" yaml:"clientID"`

    // ClientSecret is the application's secret.
    ClientSecret string `json:"-" yaml:"clientSecret"`

    // Endpoint contains the resource server's token endpoint
    // URLs. These are constants specific to each server and are
    // often available via site-specific packages, such as
    // google.Endpoint or gitlab.endpoint.
    Endpoint endpoint `json:"endpoint" yaml:"endpoint"`

    // RedirectURL is the URL to redirect users going through
    // the OAuth flow, after the resource owner's URLs.
    RedirectURL string `json:"redirectURL" yaml:"redirectURL"`

    // Used to turn off TLS certificate checks
    InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify"`

    // Scope specifies optional requested permissions.
    Scopes []string `json:"scopes" yaml:"scopes"`

    Config *oauth2.Config `json:"-" yaml:"-"`
}

// endpoint represents an OAuth 2.0 provider's authorization and token
// endpoint URLs.
type endpoint struct {
    AuthURL     string `json:"authURL" yaml:"authURL"`
    TokenURL    string `json:"tokenURL" yaml:"tokenURL"`
    UserInfoURL string `json:"userInfoURL" yaml:"userInfoURL"`
}

type Identity struct {
    Provider string `json:"provider"`
    ExternUid string `json:"extern_uid"`
}

// 根据gitab user api接口返回字段定义结构体
type gitlabIdentity struct {
    ID                int       `json:"id"`
    UserName          string    `json:"username"`
    Email             string    `json:"email"`
    Name              string    `json:"name"`
    State             string     `json:"state"`
    AvatarURL         string    `json:"avatar_url"`
    WEBURL            string    `json:"web_url"`
    CreatedAt         time.Time `json:"created_at"`
    IsAdmin           bool      `json:"is_admin"`
    Bio               string    `json:"bio"` 
    Location          string    `json:"location"` 
    Skype             string    `json:"skype"` 
    LINKEDIN          string    `json:"linkedin"`
    TWITTER           string    `json:"twitter"`
    WebsiteURL        string    `json:"website_url"`
    ORGANIZATION      string    `json:"organization"`
    LastSignInAt      time.Time `json:"last_sign_in_at"`
    ConfirmedAt       time.Time `json:"confirmed_at"`
    ThemeID           int       `json:"theme_id"`
    ColorSchemeID     int       `json:"color_scheme_id"`
    ProjectsLimits    int       `json:"projects_limit"`
    CurrentSignInAt   time.Time `json:"current_sign_in_at"`
    CanCreateGroup    bool      `json:"can_create_group"`
    CanCreateProject  bool      `json:"can_create_project"`
    TwoFactorEnabled  bool      `json:"two_factor_enabled"`
    External          bool      `json:"external"`
    Identities        []Identity `json:"identities"`
}

type gitlabProviderFactory struct {
}

func (g *gitlabProviderFactory) Type() string {
    return "GitLabIdentityProvider"
}

func (g *gitlabProviderFactory) Create(options oauth.DynamicOptions) (identityprovider.OAuthProvider, error) {
    var gitlab gitlab
    if err := mapstructure.Decode(options, &gitlab); err != nil {
        return nil, err
    }

    // fixed options
    options["endpoint"] = oauth.DynamicOptions{
        "authURL":     gitlab.Endpoint.AuthURL,
        "tokenURL":    gitlab.Endpoint.TokenURL,
        "userInfoURL": gitlab.Endpoint.UserInfoURL,
    }
    gitlab.Config = &oauth2.Config{
        ClientID:     gitlab.ClientID,
        ClientSecret: gitlab.ClientSecret,
        Endpoint: oauth2.Endpoint{
            AuthURL:  gitlab.Endpoint.AuthURL,
            TokenURL: gitlab.Endpoint.TokenURL,
        },
        RedirectURL: gitlab.RedirectURL,
        Scopes:      gitlab.Scopes,
    }
    return &gitlab, nil
}

func (g gitlabIdentity) GetUserID() string {
    return g.UserName
}

func (g gitlabIdentity) GetUsername() string {
    return g.UserName
}

func (g gitlabIdentity) GetEmail() string {
    return g.Email
}

// 请求oauth2服务端,反序列化用户信息
func (g *gitlab) IdentityExchange(code string) (identityprovider.Identity, error) {
    ctx := context.TODO()
    if g.InsecureSkipVerify {
        client := &http.Client{
            Transport: &http.Transport{
                TLSClientConfig: &tls.Config{
                    InsecureSkipVerify: true,
                },
            },
        }
        ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
    }
    token, err := g.Config.Exchange(ctx, code)
    if err != nil {
        return nil, err
    }
    resp, err := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token)).Get(g.Endpoint.UserInfoURL)
    if err != nil {
        return nil, err
    }

    data, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var gitlabIdentity gitlabIdentity
    err = json.Unmarshal(data, &gitlabIdentity)
    if err != nil {
        return nil, err
    }

    return gitlabIdentity, nil
}

改动源码后,按照kubesphere的二次开发文档重新编译生成镜像并部署

配置文件


   相对应的,ks-apiserver的配置文件kubesphere-config也要启用gitlab插件

    kubesphere.yaml: |
    authentication:
      authenticateRateLimiterMaxTries: 10
      authenticateRateLimiterDuration: 10m0s
      jwtSecret: "xxxxxxxxxxxxxxxxx"
      oauthOptions:
        accessTokenMaxAge: 1h
        accessTokenInactivityTimeout: 30m
        identityProviders:
        - name: gitlab
          type: GitLabIdentityProvider
          mappingMethod: auto
          provider:
            clientID: 'xxxxxxxxxxxxxxxx'
            clientSecret: 'xxxxxxxxxxxx'
            endpoint:
              # xxx gitlab地址
              authURL: 'http://xxx/oauth/authorize'
              tokenURL: 'http://xxx/oauth/token'
              userInfoURL: 'http://xxx/api/v4/user'
            redirectURL: 'http://xxx/oauth/redirect/gitlab'
            scopes:
              - read_user

最终效果

image.png

真香!再也不需要不胜其烦的为技术人员创建账户、找回密码了,攒下来的时间足够喝几杯咖啡了🤔

文章均为原创,关注公众号云猿生\color{green} {云猿生} 获取更多知识

image.png