kubesphere gitlab认证插件实现

为了减少管理成本,考虑利用oauth2 协议实现kubepshere和内部自建gitlab的单点登录功能

翻看kubesphere的官网文档,kubesphere3.1是支持oauth2登录功能的,并且已经内置了github和阿里云IDaas的插件,只需在ks-apiserver的配置文件中稍作更改,即可实现;但是如果需要支持基于oauth2的其他第三方认证系统,就需要开发相应的插件了,要单独开发的话,似乎就违背了方便快捷实现的初衷了

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

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

接口定义


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

image.png

接口实现


   具体实现参考了github插件的源码

   没有大的改动,主要的变动是需要重新定义一个结构体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"
)


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 接口返回字段定义结构体
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也要做相应的配置

    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

猜你喜欢

转载自juejin.im/post/7017254998060826638