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 }