前阵子搭了个Gitlab server玩,给其他人共享的时候想直接用公司LDAP进行验证登录。然后问题就来了:Gitlab需要提供一个LDAP的superuser,每次登录靠superuser使用filter查询进行验证。我没有superuser怎么办?

所以“自己动手,丰衣足食“。

说明放在 Github上,有兴趣可以尝试。

首先是gitlab的调试,为了简单,这里直接使用了Gitlab官方的docker镜像。docker run gitlab就能有Gitlab了。下面就是动手术改造LDAP模块,先docker exec -it gitlab-container-id /bin/bash进入Gitlab容器里再说。

刚开始按照官方网站配置LDAP,把东西配置好了,然后enable,接着gitlab-ctl reconfigure,刷新页面,兴高采烈地看见了登录界面有了LDAP tab。输入用户名和密码,神奇的事情就是一直500错误。

开始搜索如何debug Gitlab,Stackoverflow一下,第一次无果,但是找到如何将log从info级设置为debug级。vi $(find /opt/gitlab/embedded/service/gitlab-rails -name production.rb)。当gitlab-ctl restart后,LDAP登录500的错误竟然消失了,Gitlab这里肯定还有bug,忽略。

用了一会gitlab-ctl,help一下发现了gitlab-ctl tail可以检测log改变。好办了,开两个bash,一个tail专用,一个改代码。500错误没有了之后,就一直报Invalid credentials的错误。grep一下在哪:gems/gitlab_omniauth-ldap-1.2.1/lib/omniauth/strategies/ldap.rb。发现了Gitlab比较诡异的地方,每次LDAP登录,它要先用配置里的用户去查询登录的用户信息然后bind去验证,有点复杂…

@ldap_user_info = @adaptor.bind_as(:filter => filter(@adaptor), :size => 1, :password => request[‘password’])

顺藤摸瓜,@adaptor.bind_as,嗯,至少有了函数名,grep后发现gems/gitlab_omniauth-ldap-1.2.1/lib/omniauth-ldap/adaptor.rb。刚开始还以为ruby-net-ldap库的版本太低,于是export GEM_HOME并修改Gemfile.lock,install了一个0.15.0替换原来的0.12.1,然并卵。那只能把bind_as干掉重新写一个了:

def bind_as(args={})
    result = {}
    result[:uid] = ["#{args[:username]}"]
    result[:email] = ["#{args[:username]}@example.com"]
    login = "#{args[:username]}@example.com"
    password = args[:password]
    config = {
      :host => @host,
      :port => @port,
      :encryption => method,
      :base => @base
      :auth => {}
    }
    config[:auth][:method] = @bind_method
    config[:auth][:username] = login
    config[:auth][:password] = password
    @connection = Net::LDAP.new(config)
    false
    result if @connection.bind
  end

重启Gitlab,LDAP登录——错误:nil没有provider。没有任何头绪。偶然在tail的log里发现了email is invalid,grep!找到了gitlab-rails/lib/gitlab/o_auth/user.rb。上面的代码最初result[:email] = ["#{args[:username]}@example.com"]不是数组,log.info输出了每次的email发现都是username的第一个字符。改好以后还是不能登录,user在save的时候一直错。gitlab-rails/lib/gitlab/ldap/user.rb里发现继承了o_auth的类,gl_user获取的时候有find_by_uid_and_provider,之后就是find_by_email,那就把find_by_uid_and_provider删除吧。

万分欢喜,终于pass了登录的所有步骤。我是先注册了一个非LDAP用户,但是用户名和邮箱都是用的自己的,那么find_by_email就应该会找到这个注册的用户。果然如我所料,登录用户名密码输入正确,还是没有登录,显示用户已经被block,难道我的LDAP已经被公司封了,我没有写js脚本 T_T。

用root用户登录,发现注册的账户已经被block,是LDAP那边的信息,但我还能照常使用公司其他app。就是Gitlab有check咯。grep block,发现了主要的文件gitlab-rails/lib/gitlab/ldap/access.rb,把里面的allowed判断直接return true就好了。要是有兴趣的童鞋,可以再写点代码,连接一下LDAP server,用search方法去搜索好像是叫ee的key的value,然后返回true或false。这里就简单粗暴了。

重启,登录,完成!以为可以喝杯茶了,git clonegit push又挂了。从错误信息grep找到流程:gitlab-rails/app/controllers/projects/git_http_controller.rb => gitlab-rails/lib/gitlab/auth.rb => gitlab-rails/lib/gitlab/ldap/authentication.rb。发现了filter的残党,改为username直接传。find_by_uid_and_provider也改为find_by_email。重启,登录,创建项目,clone,push。

Gitlab 至此手术结束。