mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
generate user key for new cluster and add it to peers on bootstrap machine
This commit is contained in:
@@ -61,6 +61,7 @@ func add(ctx context.Context, uncli *cli.CLI, host string, opts addOptions) erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create default cluster: %w", err)
|
return fmt.Errorf("create default cluster: %w", err)
|
||||||
}
|
}
|
||||||
|
fmt.Printf("Created %q cluster\n", cluster.Name)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("get current cluster: %w", err)
|
return fmt.Errorf("get current cluster: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-9
@@ -9,6 +9,7 @@ import (
|
|||||||
"uncloud/internal/cli/config"
|
"uncloud/internal/cli/config"
|
||||||
"uncloud/internal/cmdexec"
|
"uncloud/internal/cmdexec"
|
||||||
"uncloud/internal/machine"
|
"uncloud/internal/machine"
|
||||||
|
"uncloud/internal/machine/network"
|
||||||
"uncloud/internal/secret"
|
"uncloud/internal/secret"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ func (c *Cluster) toConfig() *config.Cluster {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *CLI) CreateCluster(name string, privateKey ed25519.PrivateKey) (*Cluster, error) {
|
func (cli *CLI) CreateCluster(name string, privateKey ed25519.PrivateKey, userPrivateKey secret.Secret) (*Cluster, error) {
|
||||||
if _, ok := cli.config.Clusters[name]; ok {
|
if _, ok := cli.config.Clusters[name]; ok {
|
||||||
return nil, fmt.Errorf("cluster %q already exists", name)
|
return nil, fmt.Errorf("cluster %q already exists", name)
|
||||||
}
|
}
|
||||||
@@ -41,13 +42,22 @@ func (cli *CLI) CreateCluster(name string, privateKey ed25519.PrivateKey) (*Clus
|
|||||||
return nil, fmt.Errorf("generate cluster secret: %w", err)
|
return nil, fmt.Errorf("generate cluster secret: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if userPrivateKey == nil {
|
||||||
|
user, err := NewUser(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("generate user: %w", err)
|
||||||
|
}
|
||||||
|
userPrivateKey = user.PrivateKey()
|
||||||
|
}
|
||||||
|
|
||||||
c := &Cluster{
|
c := &Cluster{
|
||||||
Name: name,
|
Name: name,
|
||||||
privateKey: privateKey,
|
privateKey: privateKey,
|
||||||
config: cli.config,
|
config: cli.config,
|
||||||
}
|
}
|
||||||
cli.config.Clusters[name] = c.toConfig()
|
cfg := c.toConfig()
|
||||||
|
cfg.UserKey = userPrivateKey
|
||||||
|
cli.config.Clusters[name] = cfg
|
||||||
if err := cli.config.Save(); err != nil {
|
if err := cli.config.Save(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -56,7 +66,7 @@ func (cli *CLI) CreateCluster(name string, privateKey ed25519.PrivateKey) (*Clus
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cli *CLI) CreateDefaultCluster() (*Cluster, error) {
|
func (cli *CLI) CreateDefaultCluster() (*Cluster, error) {
|
||||||
c, err := cli.CreateCluster("default", nil)
|
c, err := cli.CreateCluster("default", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -116,7 +126,19 @@ func (c *Cluster) AddMachine(ctx context.Context, name, user, host string, port
|
|||||||
_ = exec.Close()
|
_ = exec.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
mcfg, err := machine.NewBootstrapConfig(name, netip.Prefix{})
|
wgUserKey := c.config.Clusters[c.Name].UserKey
|
||||||
|
if wgUserKey == nil {
|
||||||
|
return "", errors.New("cluster user_key must be set in the config")
|
||||||
|
}
|
||||||
|
wgUser, err := NewUser(wgUserKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("create user from key: %w", err)
|
||||||
|
}
|
||||||
|
userPeerCfg := network.PeerConfig{
|
||||||
|
Subnet: netip.PrefixFrom(wgUser.Address(), 128),
|
||||||
|
PublicKey: wgUser.PublicKey(),
|
||||||
|
}
|
||||||
|
mcfg, err := machine.NewBootstrapConfig(name, netip.Prefix{}, userPeerCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("generate machine bootstrap config: %w", err)
|
return "", fmt.Errorf("generate machine bootstrap config: %w", err)
|
||||||
}
|
}
|
||||||
@@ -153,13 +175,14 @@ func (c *Cluster) AddMachine(ctx context.Context, name, user, host string, port
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("start uncloudd: %w: %s", err, out)
|
return "", fmt.Errorf("start uncloudd: %w: %s", err, out)
|
||||||
}
|
}
|
||||||
fmt.Println("uncloudd started")
|
fmt.Println("uncloudd daemon started")
|
||||||
|
|
||||||
connConfig := config.MachineConnection{
|
connConfig := config.MachineConnection{
|
||||||
User: user,
|
User: user,
|
||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
SSHKey: sshKeyPath,
|
SSHKey: sshKeyPath,
|
||||||
|
PublicKey: mcfg.Network.PublicKey,
|
||||||
}
|
}
|
||||||
c.config.Clusters[c.Name].Machines = append(c.config.Clusters[c.Name].Machines, connConfig)
|
c.config.Clusters[c.Name].Machines = append(c.config.Clusters[c.Name].Machines, connConfig)
|
||||||
if err = c.config.Save(); err != nil {
|
if err = c.config.Save(); err != nil {
|
||||||
|
|||||||
@@ -6,4 +6,6 @@ type Cluster struct {
|
|||||||
Name string `toml:"-"`
|
Name string `toml:"-"`
|
||||||
Machines []MachineConnection `toml:"machines"`
|
Machines []MachineConnection `toml:"machines"`
|
||||||
Secret secret.Secret `toml:"secret"`
|
Secret secret.Secret `toml:"secret"`
|
||||||
|
// UserKey is the user's WireGuard private key used to connect to cluster machines.
|
||||||
|
UserKey secret.Secret `toml:"user_key"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
import "uncloud/internal/secret"
|
||||||
|
|
||||||
type MachineConnection struct {
|
type MachineConnection struct {
|
||||||
User string `toml:"user,omitempty"`
|
User string `toml:"user,omitempty"`
|
||||||
Host string `toml:"host"`
|
Host string `toml:"host"`
|
||||||
Port int `toml:"port"`
|
Port int `toml:"port"`
|
||||||
SSHKey string `toml:"ssh_key,omitempty"`
|
SSHKey string `toml:"ssh_key,omitempty"`
|
||||||
|
PublicKey secret.Secret `toml:"public_key,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
"net/netip"
|
||||||
|
"uncloud/internal/secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
privateKey wgtypes.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUser(privateKey secret.Secret) (*User, error) {
|
||||||
|
var (
|
||||||
|
wgKey wgtypes.Key
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if privateKey == nil {
|
||||||
|
wgKey, err = wgtypes.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("generate key for user: %w", err)
|
||||||
|
}
|
||||||
|
privateKey = wgKey[:]
|
||||||
|
} else {
|
||||||
|
wgKey, err = wgtypes.NewKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid key: %w", err)
|
||||||
|
}
|
||||||
|
privateKey = wgKey[:]
|
||||||
|
}
|
||||||
|
return &User{
|
||||||
|
privateKey: wgKey,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) PrivateKey() secret.Secret {
|
||||||
|
return u.privateKey[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) PublicKey() secret.Secret {
|
||||||
|
pubKey := u.privateKey.PublicKey()
|
||||||
|
return pubKey[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) Address() netip.Addr {
|
||||||
|
pubKey := u.PublicKey()
|
||||||
|
bytes := [16]byte{0xfd, 0xcc}
|
||||||
|
copy(bytes[2:], pubKey[:14])
|
||||||
|
return netip.AddrFrom16(bytes)
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ func ConfigPath(dataDir string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewBootstrapConfig returns a new machine configuration that should be applied to the first machine in a cluster.
|
// NewBootstrapConfig returns a new machine configuration that should be applied to the first machine in a cluster.
|
||||||
func NewBootstrapConfig(name string, subnet netip.Prefix) (*Config, error) {
|
func NewBootstrapConfig(name string, subnet netip.Prefix, peers ...network.PeerConfig) (*Config, error) {
|
||||||
mid, err := secret.NewID()
|
mid, err := secret.NewID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("generate machine ID: %w", err)
|
return nil, fmt.Errorf("generate machine ID: %w", err)
|
||||||
@@ -75,6 +75,7 @@ func NewBootstrapConfig(name string, subnet netip.Prefix) (*Config, error) {
|
|||||||
Subnet: subnet,
|
Subnet: subnet,
|
||||||
PrivateKey: privKey,
|
PrivateKey: privKey,
|
||||||
PublicKey: pubKey,
|
PublicKey: pubKey,
|
||||||
|
Peers: peers,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ type Config struct {
|
|||||||
Subnet netip.Prefix
|
Subnet netip.Prefix
|
||||||
PrivateKey secret.Secret
|
PrivateKey secret.Secret
|
||||||
PublicKey secret.Secret
|
PublicKey secret.Secret
|
||||||
Peers []PeerConfig
|
Peers []PeerConfig `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerConfig struct {
|
type PeerConfig struct {
|
||||||
Subnet netip.Prefix
|
Subnet netip.Prefix
|
||||||
Endpoint netip.AddrPort
|
Endpoint *netip.AddrPort `json:",omitempty"`
|
||||||
AllEndpoints []netip.AddrPort
|
AllEndpoints []netip.AddrPort `json:",omitempty"`
|
||||||
PublicKey secret.Secret
|
PublicKey secret.Secret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,17 +43,18 @@ func (c Config) toDeviceConfig() (wgtypes.Config, error) {
|
|||||||
if kErr != nil {
|
if kErr != nil {
|
||||||
return wgtypes.Config{}, fmt.Errorf("parse peer public key: %w", kErr)
|
return wgtypes.Config{}, fmt.Errorf("parse peer public key: %w", kErr)
|
||||||
}
|
}
|
||||||
endpoint := &net.UDPAddr{
|
|
||||||
IP: peerConfig.Endpoint.Addr().AsSlice(),
|
|
||||||
Port: int(peerConfig.Endpoint.Port()),
|
|
||||||
}
|
|
||||||
wgPeerConfigs[i] = wgtypes.PeerConfig{
|
wgPeerConfigs[i] = wgtypes.PeerConfig{
|
||||||
PublicKey: peerPublicKey,
|
PublicKey: peerPublicKey,
|
||||||
Endpoint: endpoint,
|
|
||||||
ReplaceAllowedIPs: true,
|
ReplaceAllowedIPs: true,
|
||||||
AllowedIPs: []net.IPNet{prefixToIPNet(peerConfig.Subnet)},
|
AllowedIPs: []net.IPNet{prefixToIPNet(peerConfig.Subnet)},
|
||||||
PersistentKeepaliveInterval: &persistentKeepalive,
|
PersistentKeepaliveInterval: &persistentKeepalive,
|
||||||
}
|
}
|
||||||
|
if peerConfig.Endpoint != nil {
|
||||||
|
wgPeerConfigs[i].Endpoint = &net.UDPAddr{
|
||||||
|
IP: peerConfig.Endpoint.Addr().AsSlice(),
|
||||||
|
Port: int(peerConfig.Endpoint.Port()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return wgtypes.Config{
|
return wgtypes.Config{
|
||||||
|
|||||||
Reference in New Issue
Block a user