Skip to content

Commit

Permalink
routes: add routes list command (#487)
Browse files Browse the repository at this point in the history
* routes: add routes list command

* handle missing dir in jwt cache
  • Loading branch information
calebdoxsey authored Jan 27, 2025
1 parent 8723bbb commit 3cd6e58
Show file tree
Hide file tree
Showing 22 changed files with 2,024 additions and 650 deletions.
49 changes: 49 additions & 0 deletions api/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package api

import (
"context"

"google.golang.org/protobuf/proto"

"github.com/pomerium/cli/internal/portal"
pb "github.com/pomerium/cli/proto"
)

func (srv *server) FetchRoutes(
ctx context.Context,
req *pb.FetchRoutesRequest,
) (*pb.FetchRoutesResponse, error) {
tlsConfig, err := getTLSConfig(req)
if err != nil {
return nil, err
}

p := portal.New(
portal.WithBrowserCommand(srv.browserCmd),
portal.WithServiceAccount(srv.serviceAccount),
portal.WithServiceAccountFile(srv.serviceAccountFile),
portal.WithTLSConfig(tlsConfig),
)

routes, err := p.ListRoutes(ctx, req.GetServerUrl())
if err != nil {
return nil, err
}

res := new(pb.FetchRoutesResponse)
for _, route := range routes {
r := &pb.PortalRoute{
Id: route.ID,
Name: route.Name,
Type: route.Type,
From: route.From,
Description: route.Description,
LogoUrl: route.LogoURL,
}
if route.ConnectCommand != "" {
r.ConnectCommand = proto.String(route.ConnectCommand)
}
res.Routes = append(res.Routes, r)
}
return res, nil
}
53 changes: 53 additions & 0 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ package api

import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"sync"

"github.com/golang/groupcache/lru"

"github.com/pomerium/cli/certstore"
pb "github.com/pomerium/cli/proto"
"github.com/pomerium/cli/tunnel"
)
Expand Down Expand Up @@ -146,3 +150,52 @@ func (s *MemCP) Save(data []byte) error {
s.data = data
return nil
}

type tlsOptions interface {
GetCaCert() []byte
GetClientCert() *pb.Certificate
GetClientCertFromStore() *pb.ClientCertFromStore
GetDisableTlsVerification() bool
}

func getTLSConfig(conn tlsOptions) (*tls.Config, error) {
cfg := &tls.Config{
//nolint: gosec
InsecureSkipVerify: conn.GetDisableTlsVerification(),
}

if c := conn.GetClientCert(); c != nil {
if len(c.Cert) == 0 {
return nil, fmt.Errorf("client cert: certificate is missing")
}
if len(c.Key) == 0 {
return nil, fmt.Errorf("client cert: key is missing")
}
cert, err := tls.X509KeyPair(c.Cert, c.Key)
if err != nil {
return nil, fmt.Errorf("client cert: %w", err)
}
cfg.Certificates = append(cfg.Certificates, cert)
}
if c := conn.GetClientCertFromStore(); c != nil {
f, err := certstore.GetClientCertificateFunc(c.GetIssuerFilter(), c.GetSubjectFilter())
if err != nil {
return nil, fmt.Errorf("client cert from store: %w", err)
}
cfg.GetClientCertificate = f
}

if len(conn.GetCaCert()) == 0 {
return cfg, nil
}

rootCA, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("get system cert pool: %w", err)
}
if ok := rootCA.AppendCertsFromPEM(conn.GetCaCert()); !ok {
return nil, fmt.Errorf("failed to append provided certificate")
}
cfg.RootCAs = rootCA
return cfg, nil
}
44 changes: 0 additions & 44 deletions api/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package api
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
Expand All @@ -16,7 +15,6 @@ import (
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/pomerium/cli/certstore"
pb "github.com/pomerium/cli/proto"
"github.com/pomerium/cli/tunnel"
)
Expand Down Expand Up @@ -84,48 +82,6 @@ func getProxy(conn *pb.Connection) (*url.URL, error) {
return u, nil
}

func getTLSConfig(conn *pb.Connection) (*tls.Config, error) {
cfg := &tls.Config{
//nolint: gosec
InsecureSkipVerify: conn.GetDisableTlsVerification(),
}

if conn.ClientCert != nil {
if len(conn.ClientCert.Cert) == 0 {
return nil, fmt.Errorf("client cert: certificate is missing")
}
if len(conn.ClientCert.Key) == 0 {
return nil, fmt.Errorf("client cert: key is missing")
}
cert, err := tls.X509KeyPair(conn.ClientCert.Cert, conn.ClientCert.Key)
if err != nil {
return nil, fmt.Errorf("client cert: %w", err)
}
cfg.Certificates = append(cfg.Certificates, cert)
}
if c := conn.ClientCertFromStore; c != nil {
f, err := certstore.GetClientCertificateFunc(c.GetIssuerFilter(), c.GetSubjectFilter())
if err != nil {
return nil, fmt.Errorf("client cert from store: %w", err)
}
cfg.GetClientCertificate = f
}

if len(conn.GetCaCert()) == 0 {
return cfg, nil
}

rootCA, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("get system cert pool: %w", err)
}
if ok := rootCA.AppendCertsFromPEM(conn.GetCaCert()); !ok {
return nil, fmt.Errorf("failed to append provided certificate")
}
cfg.RootCAs = rootCA
return cfg, nil
}

func tunnelAcceptLoop(ctx context.Context, id string, li net.Listener, tun Tunnel, b EventBroadcaster) {
evt := &tunnelEvents{EventBroadcaster: b, id: id}
evt.onListening(ctx)
Expand Down
32 changes: 4 additions & 28 deletions authclient/authclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
"net/url"
"os"
"strings"
"time"

"golang.org/x/sync/errgroup"

"github.com/pomerium/cli/internal/httputil"
)

// An AuthClient retrieves an authentication JWT via the Pomerium login API.
Expand Down Expand Up @@ -40,7 +41,7 @@ func (client *AuthClient) CheckBearerToken(ctx context.Context, serverURL *url.U
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+bearerToken)

_, err = client.fetch(ctx, req)
_, err = httputil.Fetch(ctx, client.cfg.tlsConfig, req)
return err
}

Expand Down Expand Up @@ -134,7 +135,7 @@ func (client *AuthClient) runOpenBrowser(ctx context.Context, li net.Listener, s
return err
}

bs, err := client.fetch(ctx, req)
bs, err := httputil.Fetch(ctx, client.cfg.tlsConfig, req)
if err != nil {
return err
}
Expand All @@ -149,31 +150,6 @@ func (client *AuthClient) runOpenBrowser(ctx context.Context, li net.Listener, s
return nil
}

func (client *AuthClient) fetch(ctx context.Context, req *http.Request) ([]byte, error) {
ctx, clearTimeout := context.WithTimeout(ctx, 10*time.Second)
defer clearTimeout()
req = req.WithContext(ctx)

transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = client.cfg.tlsConfig

hc := &http.Client{
Transport: transport,
}

res, err := hc.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to get url: %w", err)
}
defer func() { _ = res.Body.Close() }()

if res.StatusCode/100 != 2 {
return nil, fmt.Errorf("unexpected status code: %s", res.Status)
}

return io.ReadAll(res.Body)
}

func getBrowserURL(serverURL *url.URL) *url.URL {
browserURL := new(url.URL)
*browserURL = *serverURL
Expand Down
22 changes: 22 additions & 0 deletions cmd/pomerium-cli/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,25 @@ func saveCachedCredential(serverURL string, creds *ExecCredential) error {

return nil
}

func loadLastURL() string {
fn, err := cache.LastURLPath()
if err != nil {
return ""
}

bs, err := os.ReadFile(fn)
if err != nil {
return ""
}
return string(bs)
}

func cacheLastURL(rawServerURL string) {
fn, err := cache.LastURLPath()
if err != nil {
return
}

_ = os.WriteFile(fn, []byte(rawServerURL), 0o644)
}
2 changes: 2 additions & 0 deletions cmd/pomerium-cli/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ var kubernetesExecCredentialCmd = &cobra.Command{
return fmt.Errorf("server url is required")
}

cacheLastURL(args[0])

serverURL, err := url.Parse(args[0])
if err != nil {
return fmt.Errorf("invalid server url: %v", err)
Expand Down
71 changes: 71 additions & 0 deletions cmd/pomerium-cli/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package main

import (
"fmt"

"github.com/spf13/cobra"

"github.com/pomerium/cli/internal/portal"
)

func init() {
addBrowserFlags(routesListCmd)
addServiceAccountFlags(routesListCmd)
addTLSFlags(routesListCmd)
routesCmd.AddCommand(routesListCmd)
rootCmd.AddCommand(routesCmd)
}

var routesCmd = &cobra.Command{
Use: "routes",
Short: "commands for routes",
}

var routesListCmd = &cobra.Command{
Use: "list server-url",
Short: "list routes",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
rawServerURL := ""
if len(args) > 0 {
rawServerURL = args[0]
} else {
rawServerURL = loadLastURL()
}
if rawServerURL == "" {
return fmt.Errorf("server-url is required")
}

cacheLastURL(rawServerURL)

tlsConfig, err := getTLSConfig()
if err != nil {
return err
}

p := portal.New(
portal.WithBrowserCommand(browserOptions.command),
portal.WithServiceAccount(serviceAccountOptions.serviceAccount),
portal.WithServiceAccountFile(serviceAccountOptions.serviceAccountFile),
portal.WithTLSConfig(tlsConfig),
)
routes, err := p.ListRoutes(cmd.Context(), rawServerURL)
if err != nil {
return err
}

for _, route := range routes {
fmt.Println("Route", route.Name)
fmt.Println("id:", route.ID)
fmt.Println("type:", route.Type)
fmt.Println("from:", route.From)
fmt.Println("description:", route.Description)
if route.ConnectCommand != "" {
fmt.Println("connect_command:", route.ConnectCommand)
}
fmt.Println()
}

return nil
},
}
1 change: 1 addition & 0 deletions cmd/pomerium-cli/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var tcpCmd = &cobra.Command{
if err != nil {
return err
}
cacheLastURL(proxyURL.String())

var tlsConfig *tls.Config
if proxyURL.Scheme == "https" {
Expand Down
1 change: 1 addition & 0 deletions cmd/pomerium-cli/udp.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var udpCmd = &cobra.Command{
if err != nil {
return err
}
cacheLastURL(proxyURL.String())

var tlsConfig *tls.Config
if proxyURL.Scheme == "https" {
Expand Down
Loading

0 comments on commit 3cd6e58

Please sign in to comment.