github.com/pluralsh/plural-cli@v0.9.5/cmd/plural/vpn.go (about)

     1  package plural
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strconv"
     8  
     9  	"github.com/olekukonko/tablewriter"
    10  	"github.com/pluralsh/plural-cli/pkg/config"
    11  	"github.com/pluralsh/plural-cli/pkg/utils"
    12  	"github.com/pluralsh/plural-cli/pkg/utils/pathing"
    13  	"github.com/pluralsh/plural-cli/pkg/vpn"
    14  	"github.com/pluralsh/plural-operator/apis/vpn/v1alpha1"
    15  	"github.com/pluralsh/polly/algorithms"
    16  	"github.com/urfave/cli"
    17  
    18  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    19  )
    20  
    21  const (
    22  	wireguardAppName           = "wireguard"
    23  	wireguardNamespace         = "wireguard"
    24  	wireguardServerName        = "wireguard"
    25  	wireguardNotInstalledError = "wireguard is not installed. run `plural bundle list wireguard` to find the bundle to install"
    26  )
    27  
    28  func (p *Plural) vpnCommands() []cli.Command {
    29  	return []cli.Command{
    30  		{
    31  			Name:        "list",
    32  			Usage:       "list vpn resources",
    33  			Subcommands: p.vpnListCommands(),
    34  		},
    35  		{
    36  			Name:        "create",
    37  			Usage:       "commands for creating vpn resources",
    38  			Subcommands: p.vpnCreateCommands(),
    39  		},
    40  		{
    41  			Name:        "delete",
    42  			Usage:       "commands for deleting vpn resources",
    43  			Subcommands: p.vpnDeleteCommands(),
    44  		},
    45  		{
    46  			Name:      "client-config",
    47  			ArgsUsage: "NAME",
    48  			Usage:     "get the config for a vpn client for a server",
    49  			Action:    latestVersion(requireArgs(highlighted(p.vpnInstalled(initKubeconfig(p.handleWireguardPeerConfig))), []string{"NAME"})),
    50  			Flags: []cli.Flag{
    51  				cli.StringFlag{
    52  					Name:  "server",
    53  					Usage: "the vpn server to get the client config from",
    54  				},
    55  				cli.StringFlag{
    56  					Name:  "path",
    57  					Usage: "output path for the config wireguard client config. the filename will be NAME.conf",
    58  				},
    59  			},
    60  		},
    61  	}
    62  }
    63  
    64  func (p *Plural) vpnListCommands() []cli.Command {
    65  	return []cli.Command{
    66  		{
    67  			Name:   "servers",
    68  			Usage:  "lists vpn servers",
    69  			Action: latestVersion(highlighted(p.vpnInstalled(initKubeconfig(p.handleWireguardServerList)))),
    70  		},
    71  		{
    72  			Name:   "clients",
    73  			Usage:  "lists vpn clients for a server",
    74  			Action: latestVersion(highlighted(p.vpnInstalled(initKubeconfig(p.handleWireguardPeerList)))),
    75  			Flags: []cli.Flag{
    76  				cli.StringFlag{
    77  					Name:  "server",
    78  					Usage: "the vpn server to list clients for",
    79  				},
    80  			},
    81  		},
    82  	}
    83  }
    84  
    85  func (p *Plural) vpnCreateCommands() []cli.Command {
    86  	return []cli.Command{
    87  		{
    88  			Name:      "client",
    89  			ArgsUsage: "NAME",
    90  			Usage:     "create a new vpn client for a server",
    91  			Action:    latestVersion(requireArgs(highlighted(p.vpnInstalled(initKubeconfig(p.handleWireguardPeerCreate))), []string{"NAME"})),
    92  			Flags: []cli.Flag{
    93  				cli.StringFlag{
    94  					Name:  "server",
    95  					Usage: "the vpn server to create the client for",
    96  				},
    97  			},
    98  		},
    99  	}
   100  }
   101  
   102  func (p *Plural) vpnDeleteCommands() []cli.Command {
   103  	return []cli.Command{
   104  		{
   105  			Name:      "client",
   106  			ArgsUsage: "NAME",
   107  			Usage:     "delete a vpn client for a server",
   108  			Action:    latestVersion(requireArgs(highlighted(p.vpnInstalled(initKubeconfig(p.handleWireguardPeerDelete))), []string{"NAME"})),
   109  			Flags: []cli.Flag{
   110  				cli.StringFlag{
   111  					Name:  "server",
   112  					Usage: "the vpn server to delete the clients from",
   113  				},
   114  			},
   115  		},
   116  	}
   117  }
   118  
   119  func (p *Plural) handleWireguardServerList(c *cli.Context) error {
   120  	conf := config.Read()
   121  	servers, err := vpn.ListServers(p.Kube, conf.Namespace(wireguardNamespace))
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	headers := []string{"Name", "Hostname", "Port", "Ready"}
   127  	return utils.PrintTable(servers.Items, headers, func(s v1alpha1.WireguardServer) ([]string, error) {
   128  		return []string{s.Name, s.Status.Hostname, s.Status.Port, strconv.FormatBool(s.Status.Ready)}, nil
   129  	})
   130  }
   131  
   132  func (p *Plural) handleWireguardPeerList(c *cli.Context) error {
   133  	var server string
   134  	server = wireguardServerName
   135  	if c.String("server") != "" {
   136  		server = c.String("server")
   137  	}
   138  
   139  	conf := config.Read()
   140  	peerlist, err := vpn.ListPeers(p.Kube, conf.Namespace(wireguardNamespace))
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	peers := algorithms.Filter(peerlist.Items, func(p v1alpha1.WireguardPeer) bool { return p.Spec.WireguardRef == server })
   146  	headers := []string{"Name", "Address", "Config Secret", "Public Key", "Ready"}
   147  	return utils.PrintTable(peers, headers, func(p v1alpha1.WireguardPeer) (res []string, err error) {
   148  		res = []string{p.Name, p.Spec.Address, p.Status.ConfigRef.Name, p.Spec.PublicKey, strconv.FormatBool(p.Status.Ready)}
   149  		return
   150  	})
   151  }
   152  
   153  func (p *Plural) handleWireguardPeerCreate(c *cli.Context) error {
   154  	var serverName string
   155  	serverName = wireguardServerName
   156  	if c.String("server") != "" {
   157  		serverName = c.String("server")
   158  	}
   159  
   160  	name := c.Args().Get(0)
   161  	conf := config.Read()
   162  	server, err := vpn.GetServer(p.Kube, conf.Namespace(wireguardNamespace), serverName)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	peer, err := vpn.CreatePeer(p.Kube, server.Namespace,
   167  		&v1alpha1.WireguardPeer{
   168  			ObjectMeta: metav1.ObjectMeta{
   169  				Name: name,
   170  			},
   171  			Spec: v1alpha1.WireguardPeerSpec{
   172  				WireguardRef: server.Name,
   173  			},
   174  		})
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	table := tablewriter.NewWriter(os.Stdout)
   180  	table.SetHeader([]string{"Name", "Address", "Server", "Config Secret", "Public Key", "Ready"})
   181  	table.Append([]string{
   182  		peer.Name,
   183  		peer.Spec.Address,
   184  		peer.Spec.WireguardRef,
   185  		peer.Status.ConfigRef.Name,
   186  		peer.Spec.PublicKey,
   187  		strconv.FormatBool(peer.Status.Ready),
   188  	})
   189  	table.Render()
   190  	return nil
   191  }
   192  
   193  func (p *Plural) handleWireguardPeerConfig(c *cli.Context) error {
   194  	var serverName string
   195  	serverName = wireguardServerName
   196  	if c.String("server") != "" {
   197  		serverName = c.String("server")
   198  	}
   199  
   200  	name := c.Args().Get(0)
   201  	conf := config.Read()
   202  
   203  	server, err := vpn.GetServer(p.Kube, conf.Namespace(wireguardNamespace), serverName)
   204  	if err != nil {
   205  		return err
   206  	}
   207  
   208  	peer, err := vpn.GetPeer(p.Kube, server.Namespace, name)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	if !peer.Status.Ready || peer.Status.ConfigRef.Name == "" || peer.Status.ConfigRef.Key == "" {
   214  		return fmt.Errorf("peer config not ready yet")
   215  	}
   216  
   217  	secret, err := vpn.GetPeerConfigSecret(p.Kube, peer.Namespace, peer.Status.ConfigRef.Name)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	peerConfig, ok := secret.Data[peer.Status.ConfigRef.Key]
   223  	if !ok {
   224  		return fmt.Errorf("peer config not ready yet")
   225  	}
   226  
   227  	if c.String("path") != "" {
   228  		path := pathing.SanitizeFilepath(filepath.Join(c.String("path"), peer.Name+".conf"))
   229  		return utils.WriteFile(path, peerConfig)
   230  	}
   231  	fmt.Println(string(peerConfig))
   232  	return nil
   233  }
   234  
   235  func (p *Plural) handleWireguardPeerDelete(c *cli.Context) error {
   236  	var serverName string
   237  	serverName = wireguardServerName
   238  	if c.String("server") != "" {
   239  		serverName = c.String("server")
   240  	}
   241  
   242  	name := c.Args().Get(0)
   243  	conf := config.Read()
   244  	server, err := vpn.GetServer(p.Kube, conf.Namespace(wireguardNamespace), serverName)
   245  	if err != nil {
   246  		return err
   247  	}
   248  
   249  	peer, err := vpn.GetPeer(p.Kube, server.Namespace, name)
   250  	if err != nil {
   251  		return err
   252  	}
   253  
   254  	if err := vpn.DeletePeer(p.Kube, peer.Namespace, peer.Name); err != nil {
   255  		return err
   256  	}
   257  
   258  	utils.Highlight(fmt.Sprintf("Deleted peer %s successfully\n", peer.Name))
   259  	return nil
   260  }
   261  
   262  func (p *Plural) vpnInstalled(fn func(*cli.Context) error) func(*cli.Context) error {
   263  	return func(c *cli.Context) error {
   264  		p.InitPluralClient()
   265  		if err := p.InitKube(); err != nil {
   266  			return err
   267  		}
   268  
   269  		if _, err := p.GetInstallation(wireguardAppName); err != nil {
   270  			return err
   271  		}
   272  
   273  		return fn(c)
   274  	}
   275  }