2022-08-11 11:30:51 +00:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
edgeVPNClient "github.com/mudler/edgevpn/api/client"
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
providerConfig "github.com/kairos-io/provider-kairos/internal/provider/config"
|
2022-08-11 11:30:51 +00:00
|
|
|
"github.com/urfave/cli"
|
|
|
|
"gopkg.in/yaml.v1"
|
|
|
|
|
|
|
|
"github.com/mudler/edgevpn/api/client/service"
|
|
|
|
"github.com/mudler/edgevpn/pkg/node"
|
|
|
|
)
|
|
|
|
|
|
|
|
var networkAPI = []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "api",
|
|
|
|
Usage: "API Address",
|
|
|
|
Value: "http://localhost:8080",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "network-id",
|
2022-09-16 15:42:45 +00:00
|
|
|
Value: "kairos",
|
2022-08-11 11:30:51 +00:00
|
|
|
Usage: "Kubernetes Network Deployment ID",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-08-12 07:40:40 +00:00
|
|
|
const recoveryAddr = "127.0.0.1:2222"
|
|
|
|
|
2022-08-11 11:30:51 +00:00
|
|
|
func Start() error {
|
|
|
|
app := &cli.App{
|
2022-09-16 15:42:45 +00:00
|
|
|
Name: "kairos",
|
2022-08-11 11:30:51 +00:00
|
|
|
Version: "0.1",
|
|
|
|
Author: "Ettore Di Giacinto",
|
2022-09-16 15:42:45 +00:00
|
|
|
Usage: "kairos CLI to bootstrap, upgrade, connect and manage a kairos network",
|
2022-08-11 11:30:51 +00:00
|
|
|
Description: `
|
2022-09-16 15:42:45 +00:00
|
|
|
The kairos CLI can be used to manage a kairos box and perform all day-two tasks, like:
|
2022-08-11 11:30:51 +00:00
|
|
|
- register a node
|
|
|
|
- connect to a node in recovery mode
|
|
|
|
- to establish a VPN connection
|
|
|
|
- set, list roles
|
|
|
|
- interact with the network API
|
|
|
|
|
|
|
|
and much more.
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
For all the example cases, see: https://docs.kairos.io .
|
2022-08-11 11:30:51 +00:00
|
|
|
`,
|
|
|
|
UsageText: ``,
|
|
|
|
Copyright: "Ettore Di Giacinto",
|
|
|
|
Commands: []cli.Command{
|
2022-08-12 07:40:40 +00:00
|
|
|
{
|
|
|
|
Name: "recovery-ssh-server",
|
|
|
|
UsageText: "recovery-ssh-server",
|
|
|
|
Usage: "Starts SSH recovery service",
|
|
|
|
Description: `
|
|
|
|
Spawn up a simple standalone ssh server over p2p
|
|
|
|
`,
|
|
|
|
ArgsUsage: "Spawn up a simple standalone ssh server over p2p",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "token",
|
|
|
|
EnvVar: "TOKEN",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "service",
|
|
|
|
EnvVar: "SERVICE",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "password",
|
|
|
|
EnvVar: "PASSWORD",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "listen",
|
|
|
|
EnvVar: "LISTEN",
|
|
|
|
Value: recoveryAddr,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
return StartRecoveryService(c.String("token"), c.String("service"), c.String("password"), c.String("listen"))
|
|
|
|
},
|
|
|
|
},
|
2022-08-11 11:30:51 +00:00
|
|
|
{
|
|
|
|
Name: "register",
|
|
|
|
UsageText: "register --reboot --device /dev/sda /image/snapshot.png",
|
|
|
|
Usage: "Registers and bootstraps a node",
|
|
|
|
Description: `
|
2022-09-16 15:42:45 +00:00
|
|
|
Bootstraps a node which is started in pairing mode. It can send over a configuration file used to install the kairos node.
|
2022-08-11 11:30:51 +00:00
|
|
|
|
|
|
|
For example:
|
2022-09-16 15:42:45 +00:00
|
|
|
$ kairos register --config config.yaml --device /dev/sda ~/Downloads/screenshot.png
|
2022-08-11 11:30:51 +00:00
|
|
|
|
|
|
|
will decode the QR code from ~/Downloads/screenshot.png and bootstrap the node remotely.
|
|
|
|
|
|
|
|
If the image is omitted, a screenshot will be taken and used to decode the QR code.
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
See also https://docs.kairos.io/installation/device_pairing/ for documentation.
|
2022-08-11 11:30:51 +00:00
|
|
|
`,
|
|
|
|
ArgsUsage: "Register optionally accepts an image. If nothing is passed will take a screenshot of the screen and try to decode the QR code",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "config",
|
2022-09-19 09:59:28 +00:00
|
|
|
Usage: "Kairos YAML configuration file",
|
2022-08-11 11:30:51 +00:00
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "device",
|
|
|
|
Usage: "Device used for the installation target",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "reboot",
|
|
|
|
Usage: "Reboot node after installation",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "poweroff",
|
|
|
|
Usage: "Shutdown node after installation",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "log-level",
|
|
|
|
Usage: "Set log level",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
args := c.Args()
|
|
|
|
var ref string
|
|
|
|
if len(args) == 1 {
|
|
|
|
ref = args[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
return register(c.String("log-level"), ref, c.String("config"), c.String("device"), c.Bool("reboot"), c.Bool("poweroff"))
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "bridge",
|
|
|
|
UsageText: "bridge --network-token XXX",
|
2022-09-16 15:42:45 +00:00
|
|
|
Usage: "Connect to a kairos VPN network",
|
2022-08-11 11:30:51 +00:00
|
|
|
Description: `
|
2022-09-16 15:42:45 +00:00
|
|
|
Starts a bridge with a kairos network or a node.
|
2022-08-11 11:30:51 +00:00
|
|
|
|
|
|
|
# With a network
|
|
|
|
|
|
|
|
By default, "bridge" will create a VPN network connection to the node with the token supplied, thus it requires elevated permissions in order to work.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
$ sudo kairos bridge --network-token <TOKEN>
|
2022-08-11 11:30:51 +00:00
|
|
|
|
|
|
|
Will start a VPN, which local ip is fixed to 10.1.0.254 (tweakable with --address).
|
|
|
|
|
|
|
|
The API will also be accessible at http://127.0.0.1:8080
|
|
|
|
|
|
|
|
# With a node
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
"kairos bridge" can be used also to connect over to a node in recovery mode. When operating in this modality kairos bridge requires no specific permissions, indeed a tunnel
|
2022-08-11 11:30:51 +00:00
|
|
|
will be created locally to connect to the machine remotely.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
$ kairos bridge --qr-code-image /path/to/image.png
|
2022-08-11 11:30:51 +00:00
|
|
|
|
|
|
|
Will scan the QR code in the image and connect over. Further instructions on how to connect over will be printed out to the screen.
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
See also: https://docs.kairos.io/after_install/troubleshooting/#connect-to-the-cluster-network and https://docs.kairos.io/after_install/recovery_mode/
|
2022-08-11 11:30:51 +00:00
|
|
|
|
|
|
|
`,
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "network-token",
|
|
|
|
Required: false,
|
|
|
|
EnvVar: "NETWORK_TOKEN",
|
|
|
|
Usage: "Network token to connect over",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "log-level",
|
|
|
|
Required: false,
|
|
|
|
EnvVar: "LOGLEVEL",
|
|
|
|
Value: "info",
|
|
|
|
Usage: "Bridge log level",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "qr-code-snapshot",
|
|
|
|
Required: false,
|
|
|
|
Usage: "Bool to take a local snapshot instead of reading from an image file for recovery",
|
|
|
|
EnvVar: "QR_CODE_SNAPSHOT",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "qr-code-image",
|
|
|
|
Usage: "Path to an image containing a valid QR code for recovery mode",
|
|
|
|
Required: false,
|
|
|
|
EnvVar: "QR_CODE_IMAGE",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "api",
|
|
|
|
Value: "127.0.0.1:8080",
|
|
|
|
Usage: "Listening API url",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "dhcp",
|
|
|
|
EnvVar: "DHCP",
|
|
|
|
Usage: "Enable DHCP",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Value: "10.1.0.254/24",
|
|
|
|
Name: "address",
|
|
|
|
EnvVar: "ADDRESS",
|
|
|
|
Usage: "Specify an address for the bridge",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
2022-09-16 15:42:45 +00:00
|
|
|
Value: "/tmp/kairos",
|
2022-08-11 11:30:51 +00:00
|
|
|
Name: "lease-dir",
|
|
|
|
EnvVar: "lease-dir",
|
|
|
|
Usage: "DHCP Lease directory",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Action: bridge,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "get-kubeconfig",
|
|
|
|
Usage: "Return a deployment kubeconfig",
|
2022-09-16 15:42:45 +00:00
|
|
|
UsageText: "Retrieve a kairos network kubeconfig (only for automated deployments)",
|
2022-08-11 11:30:51 +00:00
|
|
|
Description: `
|
|
|
|
Retrieve a network kubeconfig and prints out to screen.
|
|
|
|
|
|
|
|
If a deployment was bootstrapped with a network token, you can use this command to retrieve the master node kubeconfig of a network id.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
$ kairos get-kubeconfig --network-id kairos
|
2022-08-11 11:30:51 +00:00
|
|
|
`,
|
|
|
|
Flags: networkAPI,
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
cc := service.NewClient(
|
|
|
|
c.String("network-id"),
|
|
|
|
edgeVPNClient.NewClient(edgeVPNClient.WithHost(c.String("api"))))
|
|
|
|
str, _ := cc.Get("kubeconfig", "master")
|
|
|
|
b, _ := base64.RawURLEncoding.DecodeString(str)
|
|
|
|
masterIP, _ := cc.Get("master", "ip")
|
|
|
|
fmt.Println(strings.ReplaceAll(string(b), "127.0.0.1", masterIP))
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "role",
|
|
|
|
Usage: "Set or list node roles",
|
|
|
|
Subcommands: []cli.Command{
|
|
|
|
{
|
|
|
|
Flags: networkAPI,
|
|
|
|
Name: "set",
|
|
|
|
Usage: "Set a node role",
|
2022-09-16 15:42:45 +00:00
|
|
|
UsageText: "kairos role set <UUID> master",
|
2022-08-11 11:30:51 +00:00
|
|
|
Description: `
|
|
|
|
Sets a node role propagating the setting to the network.
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
A role must be set prior to the node joining a network. You can retrieve a node UUID by running "kairos uuid".
|
2022-08-11 11:30:51 +00:00
|
|
|
|
|
|
|
Example:
|
|
|
|
|
2022-09-16 15:42:45 +00:00
|
|
|
$ (node A) kairos uuid
|
|
|
|
$ (node B) kairos role set <UUID of node A> master
|
2022-08-11 11:30:51 +00:00
|
|
|
`,
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
cc := service.NewClient(
|
|
|
|
c.String("network-id"),
|
|
|
|
edgeVPNClient.NewClient(edgeVPNClient.WithHost(c.String("api"))))
|
|
|
|
return cc.Set("role", c.Args()[0], c.Args()[1])
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Flags: networkAPI,
|
|
|
|
Name: "list",
|
|
|
|
Description: "List node roles",
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
cc := service.NewClient(
|
|
|
|
c.String("network-id"),
|
|
|
|
edgeVPNClient.NewClient(edgeVPNClient.WithHost(c.String("api"))))
|
|
|
|
advertizing, _ := cc.AdvertizingNodes()
|
|
|
|
fmt.Println("Node\tRole")
|
|
|
|
for _, a := range advertizing {
|
|
|
|
role, _ := cc.Get("role", a)
|
|
|
|
fmt.Printf("%s\t%s\n", a, role)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "create-config",
|
|
|
|
Aliases: []string{"c"},
|
|
|
|
UsageText: "Create a config with a generated network token",
|
|
|
|
|
|
|
|
Usage: "Creates a pristine config file",
|
|
|
|
Description: `
|
2022-09-16 15:42:45 +00:00
|
|
|
Prints a vanilla YAML configuration on screen which can be used to bootstrap a kairos network.
|
2022-08-11 11:30:51 +00:00
|
|
|
`,
|
|
|
|
ArgsUsage: "Optionally takes a token rotation interval (seconds)",
|
|
|
|
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
l := int(^uint(0) >> 1)
|
|
|
|
args := c.Args()
|
|
|
|
if len(args) > 0 {
|
|
|
|
if i, err := strconv.Atoi(args[0]); err == nil {
|
|
|
|
l = i
|
|
|
|
}
|
|
|
|
}
|
2022-09-19 09:59:28 +00:00
|
|
|
cc := &providerConfig.Config{Kairos: &providerConfig.Kairos{NetworkToken: node.GenerateNewConnectionData(l).Base64()}}
|
2022-08-11 11:30:51 +00:00
|
|
|
y, _ := yaml.Marshal(cc)
|
|
|
|
fmt.Println(string(y))
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "generate-token",
|
|
|
|
Aliases: []string{"g"},
|
|
|
|
UsageText: "Generate a network token",
|
|
|
|
Usage: "Creates a new token",
|
|
|
|
Description: `
|
2022-09-16 15:42:45 +00:00
|
|
|
Generates a new token which can be used to bootstrap a kairos network.
|
2022-08-11 11:30:51 +00:00
|
|
|
`,
|
|
|
|
ArgsUsage: "Optionally takes a token rotation interval (seconds)",
|
|
|
|
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
l := int(^uint(0) >> 1)
|
|
|
|
args := c.Args()
|
|
|
|
if len(args) > 0 {
|
|
|
|
if i, err := strconv.Atoi(args[0]); err == nil {
|
|
|
|
l = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Println(node.GenerateNewConnectionData(l).Base64())
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return app.Run(os.Args)
|
|
|
|
}
|