Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
babarot committed Oct 17, 2017
0 parents commit a57aaf4
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
iap_curl
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
iap_curl
========

curl wrapper for making HTTP request to IAP-protected app in CLI more easier than curl

## Usage

```console
$ export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
$ export IAP_CLIENT_ID="342624545358-asdfd8fas9df8sd7ga0sdguadfpvqp69.apps.googleusercontent.com"
$
$ iap_curl http://iap-protected.webapp.com
```

The option of iap_curl is fully compatible with curl one.

## Installation

```
$ go get github.com/b4b4r07/iap_curl
```

## License

MIT

## Author

b4b4r07
28 changes: 28 additions & 0 deletions curl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"os"
"os/exec"
"runtime"
)

func doCurl(args []string) error {
// Check if you have curl command
command := "curl"
if _, err := exec.LookPath(command); err != nil {
return err
}
for _, arg := range args {
command += " " + arg
}
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("cmd", "/c", command)
} else {
cmd = exec.Command("sh", "-c", command)
}
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
return cmd.Run()
}
114 changes: 114 additions & 0 deletions iap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package main

import (
"context"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"net/url"
"time"

"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/jws"
)

func readRsaPrivateKey(bytes []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(bytes)
if block == nil {
return nil, errors.New("invalid private key data")
}

var key *rsa.PrivateKey
var err error
if block.Type == "RSA PRIVATE KEY" {
key, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
} else if block.Type == "PRIVATE KEY" {
keyInterface, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
key, ok = keyInterface.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("not RSA private key")
}
} else {
return nil, fmt.Errorf("invalid private key type : %s", block.Type)
}

key.Precompute()

if err := key.Validate(); err != nil {
return nil, err
}

return key, nil
}

func getToken(saPath, clientID string) (token string, err error) {
sa, err := ioutil.ReadFile(saPath)
if err != nil {
return
}
conf, err := google.JWTConfigFromJSON(sa)
if err != nil {
return
}
rsaKey, _ := readRsaPrivateKey(conf.PrivateKey)
iat := time.Now()
exp := iat.Add(time.Hour)
jwt := &jws.ClaimSet{
Iss: conf.Email,
Aud: TokenURI,
Iat: iat.Unix(),
Exp: exp.Unix(),
PrivateClaims: map[string]interface{}{
"target_audience": clientID,
},
}
jwsHeader := &jws.Header{
Algorithm: "RS256",
Typ: "JWT",
}

msg, err := jws.Encode(jwsHeader, jwt, rsaKey)
if err != nil {
return
}

v := url.Values{}
v.Set("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
v.Set("assertion", msg)

ctx := context.Background()
hc := oauth2.NewClient(ctx, nil)
resp, err := hc.PostForm(TokenURI, v)
if err != nil {
return
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)

var tokenRes struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
IDToken string `json:"id_token"`
ExpiresIn int64 `json:"expires_in"`
}

if err := json.Unmarshal(body, &tokenRes); err != nil {
return token, err
}

token = tokenRes.IDToken
return
}
65 changes: 65 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"fmt"
"os"
)

const (
TokenURI = "https://www.googleapis.com/oauth2/v4/token"

GoogleApplicationCredentials = "GOOGLE_APPLICATION_CREDENTIALS"
IAPClientID = "IAP_CLIENT_ID"
)

var helpText string = `Usage: curl
`

func main() {
os.Exit(run(os.Args[1:]))
}

func run(args []string) int {
var (
creds = os.Getenv(GoogleApplicationCredentials)
clientID = os.Getenv(IAPClientID)
)

if len(args) > 0 {
if args[0] == "-h" || args[0] == "--help" {
fmt.Fprint(os.Stderr, helpText)
return 1
}
}

if creds == "" {
fmt.Fprintf(os.Stderr, "Error: %s is missing", GoogleApplicationCredentials)
return 1
}

if clientID == "" {
fmt.Fprintf(os.Stderr, "Error: %s is missing", IAPClientID)
return 1
}

token, err := getToken(creds, clientID)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err.Error())
return 1
}

authHeader := fmt.Sprintf("'Authorization: Bearer %s'", token)
curlArgs := append(
// For IAP header
[]string{"-H", authHeader},
// Original args
args...,
)

if err := doCurl(curlArgs); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err.Error())
return 1
}

return 0
}

0 comments on commit a57aaf4

Please sign in to comment.