Skip to content

Commit

Permalink
add jwt auth
Browse files Browse the repository at this point in the history
  • Loading branch information
lu1as committed Mar 6, 2022
1 parent bf88536 commit ead0160
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 40 deletions.
29 changes: 8 additions & 21 deletions cmd/terraform-backend.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"crypto/sha256"
"fmt"
"io"
"net/http"
Expand All @@ -22,14 +21,7 @@ func httpResponse(w http.ResponseWriter, code int, body string) {
fmt.Fprint(w, body)
}

func getStateID(req *http.Request) string {
vars := mux.Vars(req)
id := fmt.Sprintf("%s-%s", vars["id"], vars["project"])
hash := sha256.Sum256([]byte(id))
return fmt.Sprintf("%x", hash[:])
}

func stateHandler(stateStore store.Store, locker lock.Locker, kms kms.KMS, authenticator auth.Authenticator) func(http.ResponseWriter, *http.Request) {
func stateHandler(stateStore store.Store, locker lock.Locker, kms kms.KMS) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, req *http.Request) {
body, err := io.ReadAll(req.Body)
defer req.Body.Close()
Expand All @@ -38,20 +30,21 @@ func stateHandler(stateStore store.Store, locker lock.Locker, kms kms.KMS, authe
return
}

vars := mux.Vars(req)
state := &terraform.State{
ID: getStateID(req),
ID: terraform.GetStateID(vars["project"], vars["id"]),
}

log.Infof("%s %s", req.Method, req.URL.Path)
log.Trace("request: %s %s: %s", req.Method, req.URL.Path, body)

if ok, err := authenticator.Authenticate(req, state); err != nil {
log.Warnf("failed to evaluate request authentication for state id %s", state.ID)
httpResponse(w, http.StatusBadRequest, "Authentication missing")
if ok, err := auth.Authenticate(req, state); err != nil {
log.Warnf("failed process authentication for state id %s: %v", state.ID, err)
httpResponse(w, http.StatusForbidden, err.Error())
return
} else if !ok {
log.Warnf("failed to authenticate request for state id %s", state.ID)
httpResponse(w, http.StatusBadRequest, "Permission denied")
httpResponse(w, http.StatusForbidden, "Permission denied")
return
}

Expand Down Expand Up @@ -168,15 +161,9 @@ func main() {
}
log.Infof("initialized %s KMS backend", kms.GetName())

authenticator, err := auth.GetAuthenticator()
if err != nil {
log.Fatal(err.Error())
}
log.Infof("initialized %s auth backend", authenticator.GetName())

addr := viper.GetString("listen_addr")
log.Printf("listening on %s", addr)
r := mux.NewRouter().StrictSlash(true)
r.HandleFunc("/state/{project}/{id}", stateHandler(stateStore, locker, kms, authenticator))
r.HandleFunc("/state/{project}/{id}", stateHandler(stateStore, locker, kms))
log.Fatalf("failed to listen on %s: %v", addr, http.ListenAndServe(addr, r))
}
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/nimbolus/terraform-backend
go 1.17

require (
github.com/coreos/go-oidc/v3 v3.1.0
github.com/go-redis/redis/v8 v8.11.4
github.com/go-redsync/redsync/v4 v4.5.0
github.com/gorilla/mux v1.8.0
Expand Down Expand Up @@ -68,11 +69,13 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
google.golang.org/grpc v1.43.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
Expand Down
11 changes: 9 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
Expand Down Expand Up @@ -502,8 +504,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -565,6 +568,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
Expand All @@ -583,8 +587,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand All @@ -601,6 +606,7 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -801,6 +807,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
Expand Down
27 changes: 20 additions & 7 deletions terraform/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,39 @@ import (

"github.com/nimbolus/terraform-backend/terraform"
"github.com/nimbolus/terraform-backend/terraform/auth/basic"
"github.com/nimbolus/terraform-backend/terraform/auth/jwt"
"github.com/spf13/viper"
)

type Authenticator interface {
GetName() string
Authenticate(*http.Request, *terraform.State) (bool, error)
Authenticate(secret string, s *terraform.State) (bool, error)
}

func GetAuthenticator() (a Authenticator, err error) {
viper.SetDefault("auth_backend", "basic")
backend := viper.GetString("auth_backend")
func Authenticate(req *http.Request, s *terraform.State) (ok bool, err error) {
backend, secret, ok := req.BasicAuth()
if !ok {
return false, fmt.Errorf("no basic auth header found")
}

var authenticator Authenticator
switch backend {
case "basic":
a = basic.NewBasicAuth()
authenticator = basic.NewBasicAuth()
case "jwt":
issuerURL := viper.GetString("auth_jwt_oidc_issuer_url")
if addr := viper.GetString("vault_addr"); issuerURL == "" && addr != "" {
issuerURL = fmt.Sprintf("%s/v1/identity/oidc", addr)
} else {
return false, fmt.Errorf("jwt auth is not enabled")
}
authenticator = jwt.NewJWTAuth(issuerURL)
default:
err = fmt.Errorf("backend is not implemented")
}
if err != nil {
return nil, fmt.Errorf("failed to initialize auth backend %s: %v", backend, err)
return false, fmt.Errorf("failed to initialize auth backend %s: %v", backend, err)
}
return

return authenticator.Authenticate(secret, s)
}
10 changes: 2 additions & 8 deletions terraform/auth/basic/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package basic
import (
"crypto/sha256"
"fmt"
"net/http"

"github.com/nimbolus/terraform-backend/terraform"
)
Expand All @@ -18,13 +17,8 @@ func (l *BasicAuth) GetName() string {
return "basic"
}

func (b *BasicAuth) Authenticate(req *http.Request, s *terraform.State) (bool, error) {
username, password, ok := req.BasicAuth()
if !ok {
return false, fmt.Errorf("no basic auth header found")
}

id := fmt.Sprintf("%s:%s;%s", username, password, s.ID)
func (b *BasicAuth) Authenticate(secret string, s *terraform.State) (bool, error) {
id := fmt.Sprintf("%s:%s", secret, s.ID)
hash := sha256.Sum256([]byte(id))
s.ID = fmt.Sprintf("%x", hash[:])
return true, nil
Expand Down
54 changes: 54 additions & 0 deletions terraform/auth/jwt/jwt.go
Original file line number Diff line number Diff line change
@@ -1 +1,55 @@
package jwt

import (
"context"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/nimbolus/terraform-backend/terraform"
)

type JWTAuth struct {
issuerURL string
}

func NewJWTAuth(issuerURL string) *JWTAuth {
return &JWTAuth{
issuerURL: issuerURL,
}
}

func (l *JWTAuth) GetName() string {
return "jwt"
}

func (b *JWTAuth) Authenticate(secret string, s *terraform.State) (bool, error) {
provider, err := oidc.NewProvider(context.Background(), b.issuerURL)
if err != nil {
return false, err
}

verifier := provider.Verifier(&oidc.Config{
SkipClientIDCheck: true,
})

token, err := verifier.Verify(context.Background(), secret)
if err != nil {
return false, err
}

var claims struct {
TerraformBackend struct {
Project string `json:"project"`
State string `json:"state"`
} `json:"terraform-backend"`
}
if err := token.Claims(&claims); err != nil {
return false, err
}

tokenID := terraform.GetStateID(claims.TerraformBackend.Project, claims.TerraformBackend.State)
if s.ID == tokenID {
return true, nil
}

return false, nil
}
11 changes: 11 additions & 0 deletions terraform/terraform.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package terraform

import (
"crypto/sha256"
"fmt"
)

type State struct {
ID string
Data []byte
Lock []byte
}

func GetStateID(project, id string) string {
path := fmt.Sprintf("%s-%s", project, id)
hash := sha256.Sum256([]byte(path))
return fmt.Sprintf("%x", hash[:])
}

0 comments on commit ead0160

Please sign in to comment.