k8s cmd: use authclient package (#1722)

This commit is contained in:
Caleb Doxsey 2020-12-29 12:06:31 -07:00 committed by GitHub
parent ab44f6b057
commit 796ad2ded8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 134 deletions

View file

@ -5,21 +5,14 @@ import (
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url" "net/url"
"os" "os"
"time" "time"
"github.com/skratchdot/open-golang/open"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/sync/errgroup"
jose "gopkg.in/square/go-jose.v2" jose "gopkg.in/square/go-jose.v2"
"github.com/pomerium/pomerium/pkg/cryptutil" "github.com/pomerium/pomerium/internal/authclient"
) )
var kubernetesExecCredentialOption struct { var kubernetesExecCredentialOption struct {
@ -62,141 +55,33 @@ var kubernetesExecCredentialCmd = &cobra.Command{
return nil return nil
} }
// require interactive session to handle login var tlsConfig *tls.Config
if !terminal.IsTerminal(int(os.Stdin.Fd())) { if serverURL.Scheme == "https" {
return fmt.Errorf("only interactive sessions are supported") tlsConfig = getTLSConfig(
kubernetesExecCredentialOption.disableTLSVerification,
kubernetesExecCredentialOption.caCert,
kubernetesExecCredentialOption.alternateCAPath,
)
} }
li, err := net.Listen("tcp", "127.0.0.1:0") ac := authclient.New(authclient.WithTLSConfig(tlsConfig))
if err != nil { rawJWT, err := ac.GetJWT(context.Background(), serverURL)
fatalf("failed to start listener: %v", err)
}
defer li.Close()
incomingJWT := make(chan string)
eg, ctx := errgroup.WithContext(context.Background())
eg.Go(func() error {
return runHTTPServer(ctx, li, incomingJWT)
})
eg.Go(func() error {
return runOpenBrowser(ctx, li, serverURL)
})
eg.Go(func() error {
return runHandleJWT(ctx, serverURL, incomingJWT)
})
err = eg.Wait()
if err != nil { if err != nil {
fatalf("%s", err) fatalf("%s", err)
} }
creds, err = parseToken(rawJWT)
if err != nil {
fatalf("%s", err)
}
saveCachedCredential(serverURL.String(), creds)
printCreds(creds)
return nil return nil
}, },
} }
func runHTTPServer(ctx context.Context, li net.Listener, incomingJWT chan string) error {
var srv *http.Server
srv = &http.Server{
BaseContext: func(li net.Listener) context.Context {
return ctx
},
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
jwt := r.FormValue("pomerium_jwt")
if jwt == "" {
http.Error(w, "not found", http.StatusNotFound)
return
}
incomingJWT <- jwt
w.Header().Set("Content-Type", "text/plain")
io.WriteString(w, "login complete, you may close this page")
go func() { _ = srv.Shutdown(ctx) }()
}),
}
// shutdown the server when ctx is done.
go func() {
<-ctx.Done()
_ = srv.Shutdown(ctx)
}()
err := srv.Serve(li)
if err == http.ErrServerClosed {
err = nil
}
return err
}
func runOpenBrowser(ctx context.Context, li net.Listener, serverURL *url.URL) error {
dst := serverURL.ResolveReference(&url.URL{
Path: "/.pomerium/api/v1/login",
RawQuery: url.Values{
"pomerium_redirect_uri": {fmt.Sprintf("http://%s", li.Addr().String())},
}.Encode(),
})
ctx, clearTimeout := context.WithTimeout(ctx, 10*time.Second)
defer clearTimeout()
req, err := http.NewRequestWithContext(ctx, "GET", dst.String(), nil)
if err != nil {
return err
}
transport := &http.Transport{
TLSClientConfig: &tls.Config{},
}
if kubernetesExecCredentialOption.disableTLSVerification {
transport.TLSClientConfig.InsecureSkipVerify = true
}
transport.TLSClientConfig.RootCAs, err = cryptutil.GetCertPool(
kubernetesExecCredentialOption.caCert,
kubernetesExecCredentialOption.alternateCAPath,
)
if err != nil {
return err
}
client := &http.Client{
Transport: transport,
}
res, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to get login url: %w", err)
}
defer res.Body.Close()
if res.StatusCode/100 != 2 {
return fmt.Errorf("failed to get login url: %s", res.Status)
}
bs, err := ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read login url: %w", err)
}
return open.Run(string(bs))
}
func runHandleJWT(ctx context.Context, serverURL *url.URL, incomingJWT chan string) error {
var rawjwt string
select {
case <-ctx.Done():
return ctx.Err()
case rawjwt = <-incomingJWT:
}
creds, err := parseToken(rawjwt)
if err != nil {
return err
}
saveCachedCredential(serverURL.String(), creds)
printCreds(creds)
return nil
}
func parseToken(rawjwt string) (*ExecCredential, error) { func parseToken(rawjwt string) (*ExecCredential, error) {
tok, err := jose.ParseSigned(rawjwt) tok, err := jose.ParseSigned(rawjwt)
if err != nil { if err != nil {

View file

@ -1,10 +1,13 @@
package main package main
import ( import (
"crypto/tls"
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/pomerium/pomerium/pkg/cryptutil"
) )
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
@ -22,3 +25,18 @@ func fatalf(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\n", args...) fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1) os.Exit(1)
} }
func getTLSConfig(insecureSkipVerify bool, caCert, alternateCAPath string) *tls.Config {
cfg := new(tls.Config)
if insecureSkipVerify {
cfg.InsecureSkipVerify = true
}
if caCert != "" {
var err error
cfg.RootCAs, err = cryptutil.GetCertPool(caCert, alternateCAPath)
if err != nil {
fatalf("%s", err)
}
}
return cfg
}

View file

@ -63,7 +63,7 @@ var tcpCmd = &cobra.Command{
var tlsConfig *tls.Config var tlsConfig *tls.Config
if pomeriumURL.Scheme == "https" { if pomeriumURL.Scheme == "https" {
tlsConfig = new(tls.Config) tlsConfig = getTLSConfig(false, "", "")
} }
l := zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) { l := zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {

1
go.sum
View file

@ -733,6 +733,7 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=