Skip to content

ORGANIC-443. Added Okta to git-gateway #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.env
my.env
gorm.db
vendor/
git-gateway
Expand Down
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
FROM netlify/go-glide:v0.12.3
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original docker has golang 1.8. Some dep fails to build with it.

FROM golang:1.11.2-alpine3.8

ADD . /go/src/github.com/netlify/git-gateway

RUN useradd -m netlify && cd /go/src/github.com/netlify/git-gateway && make deps build && mv git-gateway /usr/local/bin/
RUN apk update && apk add wget && rm -rf /var/cache/apk/*

RUN apk add --update alpine-sdk

RUN go get github.com/Masterminds/glide

RUN adduser -D -u 1000 netlify && cd /go/src/github.com/netlify/git-gateway && make deps build && mv git-gateway /usr/local/bin/

USER netlify
CMD ["git-gateway"]
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ CHECK_FILES?=$$(go list ./... | grep -v /vendor/)
help: ## Show this help.
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

all: lint vet test build ## Run the tests and build the binary.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lint fails with original master.

Copy link
Author

@thomasyip thomasyip Nov 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works with newer golang version. It is reenabled.

all: vet test build ## Run the tests and build the binary.

build: ## Build the binary.
go build -ldflags "-X github.com/netlify/git-gateway/cmd.Version=`git rev-parse HEAD`"
Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ However, for most use cases you won’t want to require all content editors to h

Netlify’s Git Gateway lets you setup a gateway to your choice of Git provider's API ( now available with both GitHub and GitLab 🎉 ) that lets tools like Netlify CMS work with content, branches and pull requests on your users’ behalf.

The Git Gateway works with any identity service that can issue JWTs and only allows access when a JSON Web Token with sufficient permissions is present.
The Git Gateway works with some supported identity service that can issue JWTs and only allows access when a JSON Web Token with sufficient permissions is present.

To configure the gateway, see our `example.env` file

Expand All @@ -29,3 +29,32 @@ for GitLab:
/repos/:owner/:name/commits/
/repos/:owner/:name/tree/
```

**Running git-gateway to test locally**
**(Do not merge this section back to the open source project)**
**(Do not deploy it to production. It is a Proof of Concept has has not been secured. See @TODO items in code.**
**(the instruction assume Okta, and github.com)**

1. pull down this project
2. generate a `personal access token` on github. (recommended: using a test account and w/ `repo:status` and `public_repo` permission only)
https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
3. `cp example.env my.env`
4. update `GITGATEWAY_GITHUB_ACCESS_TOKEN` value in `my.env` accordingly
5. update `GITGATEWAY_GITHUB_REPO` value in `my.env` (it will be where the content being stored, eg, `owner/netlify-test`.)
6. sign up for a Dev account on Okta: https://developer.okta.com/signup/
7. create a SPA Application onto the Dev account:
a. fill out the details
b. Pick "Send ID Token directly to app (Okta Simplified)"
c. have redirect uri points to the url of your content-cms ip:port
(eg, `http://localhost:8080/admin` etc, see, https://github.com/<< your org >>/content-cms)
8. update `ISSUER` value in `my.env` accordingly (eg, `https://dev-1234.oktapreview.com/oauth2/default`)
9. update `CLIENT_ID` value in `my.env` accordingly (eg, `32q897q234q324rq42322q`)
10. install Docker and add the `localdev` network
11. inspect Dockfile and then build the docker with this command:
`docker build -t netlify/git-gateway:latest .`
12. run `git-gateway` with this command:
`docker run --rm --env-file my.env --net localdev -p 127.0.0.1:8087:8087 --expose 8087 -ti --name netlify-git-gateway "netlify/git-gateway:latest"`
13. update `config.yml` in your content-cms repo (ie, https://github.com/<< your org >>/content-cms).
change `backend.name` value to `git-gateway`
change `backend.gateway_url` value to `http://localhost:8087`
14. run `content-cms` following the README.md
33 changes: 27 additions & 6 deletions api/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"context"
"net/http"

jwt "github.com/dgrijalva/jwt-go"
"github.com/sirupsen/logrus"
"github.com/okta/okta-jwt-verifier-golang"
)

// requireAuthentication checks incoming requests for tokens presented using the Authorization header
Expand Down Expand Up @@ -35,14 +35,35 @@ func (a *API) extractBearerToken(w http.ResponseWriter, r *http.Request) (string
}

func (a *API) parseJWTClaims(bearer string, r *http.Request) (context.Context, error) {
// Reimplemented to use Okta lib
// Original validation only work for HS256 algo,
// Okta supports RS256 only which requires public key downloading and caching (key rotation)
config := getConfig(r.Context())
p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}}
token, err := p.ParseWithClaims(bearer, &GatewayClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(config.JWT.Secret), nil
})

toValidate := map[string]string{}
toValidate["aud"] = config.JWT.AUD
toValidate["cid"] = config.JWT.CID

jwtVerifierSetup := jwtverifier.JwtVerifier{
Issuer: config.JWT.Issuer,
ClaimsToValidate: toValidate,
}

verifier := jwtVerifierSetup.New()

_, err := verifier.VerifyAccessToken(bearer)

// @TODO? WARNING: Should be roles and other claims be checked here?

if err != nil {
return nil, unauthorizedError("Invalid token: %v", err)
}

return withToken(r.Context(), token), nil
logrus.Infof("parseJWTClaims passed")

// return nil, because the `github.go` is coded to send personal token
// both github oauth generates its own id, so oauth pass-thru is impossible
// we can improve the gateway to talk oauth with github.com, but we will
// still return nil here.
return nil, nil
}
2 changes: 1 addition & 1 deletion api/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (c contextKey) String() string {
}

const (
accessTokenKey = contextKey("access_token")
accessTokenKey = contextKey("token")
tokenKey = contextKey("jwt")
requestIDKey = contextKey("request_id")
configKey = contextKey("config")
Expand Down
9 changes: 8 additions & 1 deletion api/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ func (gh *GitHubGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
ctx = withProxyTarget(ctx, target)
ctx = withAccessToken(ctx, config.GitHub.AccessToken)

log := getLogEntry(r)
log.Infof("proxy.ServeHTTP: %+v\n", r.WithContext(ctx))
gh.proxy.ServeHTTP(w, r.WithContext(ctx))
}

Expand All @@ -82,8 +85,12 @@ func (gh *GitHubGateway) authenticate(w http.ResponseWriter, r *http.Request) er
claims := getClaims(ctx)
config := getConfig(ctx)

log := getLogEntry(r)
log.Infof("authenticate context: %v+", ctx)
if claims == nil {
return errors.New("Access to endpoint not allowed: no claims found in Bearer token")
// @TODO? WARNING: the check should be done in auth.go, imo.
// Having the jwt in the context (and thus, sent to github.com) is not necessary
// return errors.New("Access to endpoint not allowed: no claims found in Bearer token")
}

if !allowedRegexp.MatchString(r.URL.Path) {
Expand Down
3 changes: 3 additions & 0 deletions conf/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ type DBConfiguration struct {
// JWTConfiguration holds all the JWT related configuration.
type JWTConfiguration struct {
Secret string `json:"secret" required:"true"`
CID string `envconfig:"CLIENT_ID" json:"client_id,omitempty"`
Issuer string `envconfig:"ISSUER" json:"issuer,omitempty"`
AUD string `envconfig:"AUD" json:"aud,omitempty"`
}

// GlobalConfiguration holds all the configuration that applies to all instances.
Expand Down
30 changes: 24 additions & 6 deletions example.env
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
GITGATEWAY_JWT_SECRET="CHANGE-THIS! VERY IMPORTANT!"
# Warning: Many configuration would not work with quote (ie "").

# JWT Secret is not needed for RS256. Instead, issuer should be specified
# (eg, https://dev-1234.oktapreview.com/oauth2/default)
GITGATEWAY_JWT_SECRET=

# @TODO - REQUIRED for Okta
ISSUER=

# @TODO - REQUIRED for Okta
CLIENT_ID=

AUD=api://default

GITGATEWAY_DB_DRIVER=sqlite3
DATABASE_URL=gorm.db

GITGATEWAY_API_HOST=localhost
PORT=9999
# @TODO - Is there way to expose internal port from Docker?
GITGATEWAY_API_HOST=0.0.0.0
PORT=8087

# @TODO - REQUIRED
GITGATEWAY_GITHUB_ACCESS_TOKEN=

GITGATEWAY_GITHUB_ACCESS_TOKEN="personal-access-token"
GITGATEWAY_GITHUB_REPO="owner/name"
# @TODO - REQUIRED
GITGATEWAY_GITHUB_REPO=

GITGATEWAY_ROLES="admin,cms" # leave blank to allow all roles
# Original example.env wrote: leave blank to allow all roles. But, it won't
# work unless it is commented out
# GITGATEWAY_ROLES=
Loading