Last active
October 11, 2022 09:28
-
-
Save skurfuerst/efc1df4971d969797397c6bb76f0eb50 to your computer and use it in GitHub Desktop.
GitLab and Rancher 2 Authentication Proxy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -ex | |
go build -o rancher_gitlab_proxy main.go | |
GOOS=linux go build -o rancher_gitlab_proxy_linux main.go |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# in GitLab config - gitlab.rb | |
nginx['custom_gitlab_server_config'] = " | |
# CONNECTION TO rancher-gitlab-proxy BEGIN | |
location /login/oauth/authorize { | |
proxy_pass http://127.0.0.1:8888; | |
} | |
location /login/oauth/access_token { | |
proxy_pass http://127.0.0.1:8888; | |
} | |
location /api/v3/user { | |
proxy_pass http://127.0.0.1:8888; | |
} | |
location /api/v3/teams/ { | |
proxy_pass http://127.0.0.1:8888; | |
} | |
location /api/v3/search/users { | |
proxy_pass http://127.0.0.1:8888; | |
} | |
# CONNECTION TO rancher-gitlab-proxy END | |
" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module sandstorm.de/rancher-gitlab-proxy | |
go 1.15 | |
require ( | |
github.com/julienschmidt/httprouter v1.3.0 | |
github.com/xanzy/go-gitlab v0.40.2 | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
"github.com/julienschmidt/httprouter" | |
"github.com/xanzy/go-gitlab" | |
"net/http" | |
"strconv" | |
"strings" | |
) | |
// !!! ADJUST | |
const gitlab_url = "https://gitlab.com" | |
const rancher_url = "https://rancher-url.de" | |
// !!! ADJUST | |
///////////////// MAIN | |
func main() { | |
router := httprouter.New() | |
router.GET("/login/oauth/authorize", oauthAuthorize) | |
router.POST("/login/oauth/access_token", oauthAccessToken) | |
router.GET("/api/v3/user", apiV3User) | |
router.GET("/api/v3/user/:id", apiV3UserId) | |
router.GET("/api/v3/teams/:id", apiV3TeamsId) | |
router.GET("/api/v3/search/users", apiV3SearchUsers) | |
fmt.Println("Listening to 127.0.0.1:8888") | |
if err := http.ListenAndServe("127.0.0.1:8888", router); err != nil { | |
panic(err) | |
} | |
} | |
///////////////// API | |
func oauthAuthorize(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { | |
v := req.URL.Query() | |
v.Add("response_type", "code") | |
v.Add("scope", "read_api") | |
target := gitlab_url + "/oauth/authorize?" + v.Encode() | |
http.Redirect(w, req, target, http.StatusTemporaryRedirect) | |
} | |
func oauthAccessToken(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { | |
v := req.URL.Query() | |
v.Add("grant_type", "authorization_code") | |
v.Add("redirect_uri", rancher_url + "/verify-auth") | |
target := gitlab_url + "/oauth/token?" + v.Encode() | |
http.Redirect(w, req, target, http.StatusTemporaryRedirect) | |
} | |
func apiV3User(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { | |
gitlabClient := createGitlabClient(req) | |
gitlabUser, _, err := gitlabClient.Users.CurrentUser() | |
if err != nil { | |
panic(err) | |
} | |
githubAccount := convertGitlabUserToAccount(gitlabUser) | |
jsonStr, _ := json.Marshal(githubAccount) | |
w.Write(jsonStr) | |
} | |
func apiV3UserId(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { | |
// Workaround to deal with routing library | |
if ps.ByName("id") == "orgs" { | |
apiV3UserOrgs(w, req, ps) | |
return | |
} | |
if ps.ByName("id") == "teams" { | |
apiV3UserTeams(w, req, ps) | |
return | |
} | |
gitlabClient := createGitlabClient(req) | |
id, _ := strconv.Atoi(ps.ByName("id")) | |
// user | |
gitlabUser, _, err := gitlabClient.Users.GetUser(id) | |
if err != nil { | |
panic(err) | |
} | |
githubAccount := convertGitlabUserToAccount(gitlabUser) | |
jsonStr, _ := json.Marshal(githubAccount) | |
w.Write(jsonStr) | |
} | |
func apiV3UserOrgs(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { | |
result := make([]string, 0) | |
jsonStr, _ := json.Marshal(result) | |
w.Write(jsonStr) | |
} | |
func apiV3UserTeams(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { | |
gitlabClient := createGitlabClient(req) | |
allAvailable := false | |
result := make([]Team, 0, 0) | |
listGroupsOptions := &gitlab.ListGroupsOptions{ | |
ListOptions: gitlab.ListOptions{ | |
}, | |
// here, we only want to search for groups WHICH WE ARE MEMBER OF!!! | |
AllAvailable: &allAvailable, | |
} | |
for { | |
gitlabGroups, resp, err := gitlabClient.Groups.ListGroups(listGroupsOptions) | |
if err != nil { | |
panic(err) | |
} | |
for _, gitlabGroup := range gitlabGroups { | |
team := convertGitlabGroupToTeam(gitlabGroup) | |
result = append(result, *team) | |
} | |
// Exit the loop when we've seen all pages. | |
if resp.CurrentPage >= resp.TotalPages { | |
break | |
} | |
// Update the page number to get the next page. | |
listGroupsOptions.ListOptions.Page = resp.NextPage | |
} | |
jsonStr, _ := json.Marshal(result) | |
w.Write(jsonStr) | |
} | |
func apiV3TeamsId(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { | |
gitlabClient := createGitlabClient(req) | |
id, _ := strconv.Atoi(ps.ByName("id")) | |
gitlabGroup, _, err := gitlabClient.Groups.GetGroup(id) | |
if err != nil { | |
panic(err) | |
} | |
team := convertGitlabGroupToTeam(gitlabGroup) | |
jsonStr, _ := json.Marshal(team) | |
w.Write(jsonStr) | |
} | |
type searchResult struct { | |
Items []*Account `json:"items"` | |
} | |
func apiV3SearchUsers(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { | |
query := req.URL.Query().Get("q") | |
gitlabClient := createGitlabClient(req) | |
searchResult := &searchResult{ | |
Items: make([]*Account, 0), | |
} | |
shouldSearchUsers := true | |
shouldSearchOrgs := true | |
if strings.Contains(query, "type:org") { | |
shouldSearchUsers = false | |
shouldSearchOrgs = true | |
query = strings.ReplaceAll(query, "type:org", "") | |
} | |
if shouldSearchOrgs { | |
allAvailable := true | |
gitlabGroups, _, err := gitlabClient.Groups.ListGroups(&gitlab.ListGroupsOptions{ | |
Search: &query, | |
// we want to find ALL groups (which are not fully private) | |
AllAvailable: &allAvailable, | |
}) | |
if err != nil { | |
panic(err) | |
} | |
for _, gitlabGroup := range gitlabGroups { | |
githubOrg := convertGitlabGroupToAccount(gitlabGroup) | |
searchResult.Items = append(searchResult.Items, githubOrg) | |
} | |
} | |
if shouldSearchUsers { | |
gitlabUsers, _, err := gitlabClient.Users.ListUsers(&gitlab.ListUsersOptions{ | |
Search: &query, | |
}) | |
if err != nil { | |
panic(err) | |
} | |
for _, gitlabUser := range gitlabUsers { | |
githubAccount := convertGitlabUserToAccount(gitlabUser) | |
searchResult.Items = append(searchResult.Items, githubAccount) | |
} | |
} | |
jsonStr, _ := json.Marshal(searchResult) | |
w.Write(jsonStr) | |
} | |
///////////////// HELPERS | |
// https://docs.github.com/en/free-pro-team@latest/rest/reference/users#get-the-authenticated-user | |
// copied from https://github.com/rancher/rancher/blob/2506427ba7bd31edf12f7110b7fdb8b2defe8eb3/pkg/auth/providers/github/github_account.go#L12 | |
type Account struct { | |
ID int `json:"id,omitempty"` | |
Login string `json:"login,omitempty"` | |
Name string `json:"name,omitempty"` | |
AvatarURL string `json:"avatar_url,omitempty"` | |
HTMLURL string `json:"html_url,omitempty"` | |
// "Type" must be "user", "team", oder "org" | |
Type string `json:"type,omitempty"` | |
} | |
//Team defines properties a team on github has | |
type Team struct { | |
ID int `json:"id,omitempty"` | |
Organization map[string]interface{} `json:"organization,omitempty"` | |
Name string `json:"name,omitempty"` | |
Slug string `json:"slug,omitempty"` | |
} | |
func createGitlabClient(req *http.Request) *gitlab.Client { | |
authorizationHeader := req.Header.Get("Authorization") | |
t := strings.Split(authorizationHeader, " ") | |
token := t[1] | |
gitlabClient, err := gitlab.NewOAuthClient(token, gitlab.WithBaseURL(gitlab_url + "/api/v4")) | |
if err != nil { | |
panic(err) | |
} | |
return gitlabClient | |
} | |
func convertGitlabUserToAccount(gitlabUser *gitlab.User) *Account { | |
return &Account{ | |
ID: gitlabUser.ID, | |
Login: gitlabUser.Username, | |
Name: gitlabUser.Name, | |
AvatarURL: gitlabUser.AvatarURL, | |
HTMLURL: "", | |
Type: "user", | |
} | |
} | |
func convertGitlabGroupToAccount(gitlabGroup *gitlab.Group) *Account { | |
return &Account{ | |
ID: gitlabGroup.ID, | |
Login: gitlabGroup.Path, | |
Name: gitlabGroup.Name, | |
AvatarURL: gitlabGroup.AvatarURL, | |
HTMLURL: "", | |
Type: "team", | |
} | |
} | |
func convertGitlabGroupToTeam(gitlabGroup *gitlab.Group) *Team { | |
org := make(map[string]interface{}) | |
org["login"] = gitlabGroup.Path | |
org["avatar_url"] = gitlabGroup.AvatarURL | |
return &Team{ | |
ID: gitlabGroup.ID, | |
Organization: org, | |
Name: gitlabGroup.Name, | |
Slug: gitlabGroup.Path, | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=rancher-gitlab-proxy | |
Wants=network-online.target | |
After=network-online.target | |
[Service] | |
Type=simple | |
User=gitlab-www | |
Group=gitlab-www | |
ExecStart=/usr/local/bin/rancher_gitlab_proxy | |
SyslogIdentifier=rancher-gitlab-proxy | |
Restart=always | |
[Install] | |
WantedBy=multi-user.target |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment