Compare commits
11 Commits
8506f8807d
...
v2.2.1
Author | SHA1 | Date | |
---|---|---|---|
5ab8b74041 | |||
7b62140d2a | |||
650c913930 | |||
d69f2f27ee | |||
12bfa6cfd6 | |||
898c43b954 | |||
1555419549 | |||
3f2cd997a0 | |||
86d85f014c | |||
69cc01db9b | |||
3c7d56ae48 |
@ -1 +1,8 @@
|
||||
Dockerfile
|
||||
tmp/**/*
|
||||
dist/*
|
||||
go.work
|
||||
go.work.sum
|
||||
modd.*conf
|
||||
test-initrd*
|
||||
test-initrd/**/*
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@
|
||||
/qemu.pid
|
||||
/test-initrd.cpio
|
||||
/tmp
|
||||
/go.work
|
||||
/go.work.sum
|
||||
|
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@ -0,0 +1,32 @@
|
||||
from golang:1.21.5-alpine3.19 as build
|
||||
|
||||
workdir /src
|
||||
copy go.mod go.sum .
|
||||
run go mod download
|
||||
|
||||
copy . .
|
||||
run go test ./...
|
||||
run go build -o /go/bin/init -trimpath .
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
from alpine:3.19.0 as initrd
|
||||
|
||||
run apk add --no-cache xz
|
||||
|
||||
workdir /layer
|
||||
run wget -O- https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/x86_64/alpine-minirootfs-3.18.4-x86_64.tar.gz |tar zxv
|
||||
|
||||
run apk add --no-cache -p . musl lvm2 lvm2-dmeventd udev cryptsetup e2fsprogs btrfs-progs lsblk
|
||||
run rm -rf usr/share/apk var/cache/apk
|
||||
|
||||
copy --from=build /go/bin/init .
|
||||
|
||||
# check viability
|
||||
run chroot /layer /init hello
|
||||
|
||||
run find |cpio -H newc -o >/initrd
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
from alpine:3.19.0
|
||||
copy --from=initrd /initrd /
|
||||
entrypoint ["base64","/initrd"]
|
@ -1,12 +0,0 @@
|
||||
# ------------------------------------------------------------------------
|
||||
from alpine:3.15
|
||||
|
||||
add alpine-minirootfs-3.15.0-x86_64.tar.gz /layer/
|
||||
|
||||
workdir /layer
|
||||
|
||||
run apk update
|
||||
run apk add -p . musl lvm2 lvm2-dmeventd udev cryptsetup e2fsprogs btrfs-progs
|
||||
run rm -rf usr/share/apk var/cache/apk
|
||||
|
||||
entrypoint ["sh","-c","find |cpio -H newc -o |base64"]
|
Binary file not shown.
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
@ -38,6 +39,8 @@ func askSecret(prompt string) []byte {
|
||||
fatalf("failed to read from stdin: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
s = bytes.TrimRight(s, "\r\n")
|
||||
return s
|
||||
}
|
||||
|
3
auth.go
3
auth.go
@ -6,7 +6,8 @@ import (
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
"novit.nc/direktil/initrd/config"
|
||||
|
||||
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||
)
|
||||
|
||||
var (
|
||||
|
66
boot-v1.go
66
boot-v1.go
@ -1,17 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"novit.nc/direktil/pkg/sysfs"
|
||||
"novit.tech/direktil/pkg/sysfs"
|
||||
)
|
||||
|
||||
var loopOffset = 0
|
||||
@ -58,7 +57,7 @@ func bootV1() {
|
||||
}
|
||||
|
||||
func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
||||
cfgBytes, err := ioutil.ReadFile(cfgPath)
|
||||
cfgBytes, err := os.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
fatalf("failed to read %s: %v", cfgPath, err)
|
||||
}
|
||||
@ -105,10 +104,7 @@ func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
||||
|
||||
lowers[i] = dir
|
||||
|
||||
loopDev := fmt.Sprintf("/dev/loop%d", i+loopOffset)
|
||||
losetup(loopDev, path)
|
||||
|
||||
mount(loopDev, dir, "squashfs", layerMountFlags, "")
|
||||
mountSquahfs(path, dir)
|
||||
}
|
||||
|
||||
// prepare system root
|
||||
@ -120,6 +116,13 @@ func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
||||
mount("overlay", "/system", "overlay", rootMountFlags,
|
||||
"lowerdir="+strings.Join(lowers, ":")+",upperdir=/changes/upperdir,workdir=/changes/workdir")
|
||||
|
||||
// make root rshared (default in systemd, required by Kubernetes 1.10+)
|
||||
// equivalent to "mount --make-rshared /"
|
||||
// see kernel's Documentation/sharedsubtree.txt (search rshared)
|
||||
if err := syscall.Mount("", "/system", "", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil {
|
||||
fatalf("FATAL: mount --make-rshared / failed: %v", err)
|
||||
}
|
||||
|
||||
if bootMounted {
|
||||
if layersInMemory {
|
||||
if err := syscall.Unmount("/boot", 0); err != nil {
|
||||
@ -134,7 +137,7 @@ func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
||||
|
||||
// - write configuration
|
||||
log.Print("writing /config.yaml")
|
||||
if err := ioutil.WriteFile("/system/config.yaml", cfgBytes, 0600); err != nil {
|
||||
if err := os.WriteFile("/system/config.yaml", cfgBytes, 0600); err != nil {
|
||||
fatal("failed: ", err)
|
||||
}
|
||||
|
||||
@ -144,7 +147,50 @@ func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
||||
|
||||
filePath := filepath.Join("/system", fileDef.Path)
|
||||
|
||||
ioutil.WriteFile(filePath, []byte(fileDef.Content), fileDef.Mode)
|
||||
if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
|
||||
log.Printf("failed to create dir %s: %v", filepath.Dir(fileDef.Path), err)
|
||||
}
|
||||
|
||||
mode := fileDef.Mode
|
||||
if mode == 0 {
|
||||
mode = 0644
|
||||
}
|
||||
|
||||
err = os.WriteFile(filePath, []byte(fileDef.Content), mode)
|
||||
if err != nil {
|
||||
fatalf("failed to write %s: %v", fileDef.Path, err)
|
||||
}
|
||||
}
|
||||
|
||||
// - groups
|
||||
for _, group := range cfg.Groups {
|
||||
log.Print("creating group ", group.Name)
|
||||
|
||||
opts := make([]string, 0)
|
||||
opts = append(opts /* chroot */, "/system", "groupadd", "-r")
|
||||
if group.Gid != 0 {
|
||||
opts = append(opts, "-g", strconv.Itoa(group.Gid))
|
||||
}
|
||||
opts = append(opts, group.Name)
|
||||
|
||||
run("chroot", opts...)
|
||||
}
|
||||
|
||||
// - user
|
||||
for _, user := range cfg.Users {
|
||||
log.Print("creating user ", user.Name)
|
||||
|
||||
opts := make([]string, 0)
|
||||
opts = append(opts /* chroot */, "/system", "useradd", "-r")
|
||||
if user.Gid != 0 {
|
||||
opts = append(opts, "-g", strconv.Itoa(user.Gid))
|
||||
}
|
||||
if user.Uid != 0 {
|
||||
opts = append(opts, "-u", strconv.Itoa(user.Uid))
|
||||
}
|
||||
opts = append(opts, user.Name)
|
||||
|
||||
run("chroot", opts...)
|
||||
}
|
||||
|
||||
return
|
||||
|
29
boot-v2.go
29
boot-v2.go
@ -7,12 +7,15 @@ import (
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"novit.nc/direktil/initrd/config"
|
||||
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||
)
|
||||
|
||||
func bootV2() {
|
||||
log.Print("-- boot v2 --")
|
||||
|
||||
kernelVersion := unameRelease()
|
||||
log.Print("Linux version ", kernelVersion)
|
||||
|
||||
cfg := &config.Config{}
|
||||
|
||||
{
|
||||
@ -30,24 +33,23 @@ func bootV2() {
|
||||
}
|
||||
|
||||
log.Print("config loaded")
|
||||
log.Printf("anti-phishing code: %q", cfg.AntiPhishingCode)
|
||||
log.Printf("\n\nanti-phishing code: %q\n", cfg.AntiPhishingCode)
|
||||
|
||||
auths = cfg.Auths
|
||||
|
||||
// mount kernel modules
|
||||
if cfg.Modules != "" {
|
||||
log.Print("mount modules from ", cfg.Modules)
|
||||
if cfg.Modules == "" {
|
||||
log.Print("NOT mounting modules (nothing specified)")
|
||||
} else {
|
||||
mountSquahfs(cfg.Modules, "/modules")
|
||||
|
||||
err := os.MkdirAll("/modules", 0755)
|
||||
if err != nil {
|
||||
fatal("failed to create /modules: ", err)
|
||||
modulesSourcePath := "/modules/lib/modules/" + kernelVersion
|
||||
if _, err := os.Stat(modulesSourcePath); err != nil {
|
||||
fatal("invalid modules dir: ", err)
|
||||
}
|
||||
|
||||
run("mount", cfg.Modules, "/modules")
|
||||
loopOffset++
|
||||
|
||||
err = os.Symlink("/modules/lib/modules", "/lib/modules")
|
||||
if err != nil {
|
||||
os.MkdirAll("/lib/modules", 0755)
|
||||
if err := os.Symlink(modulesSourcePath, "/lib/modules/"+kernelVersion); err != nil {
|
||||
fatal("failed to symlink modules: ", err)
|
||||
}
|
||||
}
|
||||
@ -77,6 +79,9 @@ func bootV2() {
|
||||
// SSH service
|
||||
startSSH(cfg)
|
||||
|
||||
// dmcrypt blockdevs
|
||||
setupCrypt(cfg.PreLVMCrypt, map[string]string{})
|
||||
|
||||
// LVM
|
||||
setupLVM(cfg)
|
||||
|
||||
|
64
bootstrap.go
64
bootstrap.go
@ -2,13 +2,15 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"novit.nc/direktil/initrd/config"
|
||||
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||
)
|
||||
|
||||
func bootstrap(cfg *config.Config) {
|
||||
@ -34,14 +36,58 @@ func bootstrap(cfg *config.Config) {
|
||||
|
||||
log.Printf("seeding bootstrap from %s", seed)
|
||||
|
||||
// TODO
|
||||
err = os.MkdirAll(baseDir, 0700)
|
||||
if err != nil {
|
||||
fatalf("failed to create bootstrap dir: %v", err)
|
||||
}
|
||||
|
||||
bootstrapFile := filepath.Join(baseDir, "bootstrap.tar")
|
||||
|
||||
err = func() (err error) {
|
||||
resp, err := http.Get(seed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("bad HTTP status: %s", resp.Status)
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
out, err := os.Create(bootstrapFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
|
||||
return
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
fatalf("seeding failed: %v", err)
|
||||
}
|
||||
|
||||
log.Print("unpacking bootstrap file")
|
||||
run("tar", "xvf", bootstrapFile, "-C", baseDir)
|
||||
}
|
||||
|
||||
layersDir = baseDir
|
||||
layersOverride["modules"] = "/modules.sqfs"
|
||||
sysCfg := applyConfig(sysCfgPath, false)
|
||||
|
||||
// mounts are v2 only
|
||||
localGenDir := filepath.Join(bsDir, "local-gen")
|
||||
|
||||
// vpns are v2+
|
||||
for _, vpn := range sysCfg.VPNs {
|
||||
setupVPN(vpn, localGenDir)
|
||||
}
|
||||
|
||||
// mounts are v2+
|
||||
for _, mount := range sysCfg.Mounts {
|
||||
log.Print("mount ", mount.Dev, " to system's ", mount.Path)
|
||||
|
||||
@ -69,12 +115,15 @@ func bootstrap(cfg *config.Config) {
|
||||
log.Print("setting root's authorized keys")
|
||||
setAuthorizedKeys(ak)
|
||||
}
|
||||
|
||||
// update-ca-certificates
|
||||
run("chroot", "/system", "update-ca-certificates")
|
||||
}
|
||||
|
||||
func setUserPass(user, passwordHash string) {
|
||||
const fpath = "/system/etc/shadow"
|
||||
|
||||
ba, err := ioutil.ReadFile(fpath)
|
||||
ba, err := os.ReadFile(fpath)
|
||||
if err != nil {
|
||||
fatalf("failed to read shadow: %v", err)
|
||||
}
|
||||
@ -87,6 +136,7 @@ func setUserPass(user, passwordHash string) {
|
||||
p := strings.Split(line, ":")
|
||||
if len(p) < 2 || p[0] != user {
|
||||
buf.WriteString(line)
|
||||
buf.WriteByte('\n')
|
||||
continue
|
||||
}
|
||||
|
||||
@ -97,7 +147,7 @@ func setUserPass(user, passwordHash string) {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(fpath, buf.Bytes(), 0600)
|
||||
err = os.WriteFile(fpath, buf.Bytes(), 0600)
|
||||
if err != nil {
|
||||
fatalf("failed to write shadow: %v", err)
|
||||
}
|
||||
@ -116,7 +166,7 @@ func setAuthorizedKeys(ak []string) {
|
||||
fatalf("failed to create %s: %v", sshDir, err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(sshDir, "authorized_keys"), buf.Bytes(), 0600)
|
||||
err = os.WriteFile(filepath.Join(sshDir, "authorized_keys"), buf.Bytes(), 0600)
|
||||
if err != nil {
|
||||
fatalf("failed to write authorized keys: %v", err)
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
from golang:1.18beta2-alpine3.15
|
||||
|
||||
run apk add --update musl-dev git
|
||||
|
||||
workdir /src
|
23
config.go
23
config.go
@ -1,26 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
nconfig "novit.nc/direktil/pkg/config"
|
||||
nconfig "novit.tech/direktil/pkg/config"
|
||||
)
|
||||
|
||||
type configV1 struct {
|
||||
Layers []string `yaml:"layers"`
|
||||
Files []nconfig.FileDef `yaml:"files"`
|
||||
|
||||
// v2 handles more
|
||||
|
||||
RootUser struct {
|
||||
PasswordHash string `yaml:"password_hash"`
|
||||
AuthorizedKeys []string `yaml:"authorized_keys"`
|
||||
} `yaml:"root_user"`
|
||||
|
||||
Mounts []MountDef `yaml:"mounts"`
|
||||
}
|
||||
|
||||
type MountDef struct {
|
||||
Dev string
|
||||
Path string
|
||||
Options string
|
||||
Type string
|
||||
}
|
||||
type configV1 = nconfig.Config
|
||||
|
@ -1,61 +0,0 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
AntiPhishingCode string `json:"anti_phishing_code"`
|
||||
|
||||
Keymap string
|
||||
Modules string
|
||||
|
||||
Auths []Auth
|
||||
|
||||
Networks []struct {
|
||||
Name string
|
||||
Interfaces []struct {
|
||||
Var string
|
||||
N int
|
||||
Regexps []string
|
||||
}
|
||||
Script string
|
||||
}
|
||||
|
||||
LVM []LvmVG
|
||||
Bootstrap Bootstrap
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
Name string
|
||||
SSHKey string `yaml:"sshKey"`
|
||||
Password string `yaml:"password"`
|
||||
}
|
||||
|
||||
type LvmVG struct {
|
||||
VG string
|
||||
PVs struct {
|
||||
N int
|
||||
Regexps []string
|
||||
}
|
||||
|
||||
Defaults struct {
|
||||
FS string
|
||||
Raid *RaidConfig
|
||||
}
|
||||
|
||||
LVs []struct {
|
||||
Name string
|
||||
Crypt string
|
||||
FS string
|
||||
Raid *RaidConfig
|
||||
Size string
|
||||
Extents string
|
||||
}
|
||||
}
|
||||
|
||||
type RaidConfig struct {
|
||||
Mirrors int
|
||||
Stripes int
|
||||
}
|
||||
|
||||
type Bootstrap struct {
|
||||
Dev string
|
||||
Seed string
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
var (
|
||||
encoding = base64.RawStdEncoding
|
||||
)
|
||||
|
||||
func PasswordHashFromSeed(seed, pass []byte) string {
|
||||
h := pbkdf2.Key(pass, seed, 2048, 32, sha512.New)
|
||||
return encoding.EncodeToString(h)
|
||||
}
|
||||
|
||||
func PasswordHash(pass []byte) (hashedPassWithSeed string) {
|
||||
seed := make([]byte, 10) // 8 bytes min by the RFC recommendation
|
||||
_, err := rand.Read(seed)
|
||||
if err != nil {
|
||||
panic(err) // we do not expect this to fail...
|
||||
}
|
||||
return JoinSeedAndHash(seed, PasswordHashFromSeed(seed, pass))
|
||||
}
|
||||
|
||||
func JoinSeedAndHash(seed []byte, hash string) string {
|
||||
return encoding.EncodeToString(seed) + ":" + hash
|
||||
}
|
||||
|
||||
func CheckPassword(hashedPassWithSeed string, pass []byte) (ok bool) {
|
||||
parts := strings.SplitN(hashedPassWithSeed, ":", 2)
|
||||
|
||||
encodedSeed := parts[0]
|
||||
encodedHash := parts[1]
|
||||
|
||||
seed, err := encoding.DecodeString(encodedSeed)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return encodedHash == PasswordHashFromSeed(seed, pass)
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package config
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExamplePasswordHash() {
|
||||
seed := []byte("myseed")
|
||||
hash := PasswordHashFromSeed(seed, []byte("mypass"))
|
||||
fmt.Println(JoinSeedAndHash(seed, hash))
|
||||
|
||||
// Output:
|
||||
// bXlzZWVk:HMSxrg1cYphaPuUYUbtbl/htep/tVYYIQAuvkNMVpw0
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
package cpiocat
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/cavaliergopher/cpio"
|
||||
)
|
||||
|
||||
func Append(out io.Writer, in io.Reader, filesToAppend []string) (err error) {
|
||||
cout := cpio.NewWriter(out)
|
||||
|
||||
cin := cpio.NewReader(in)
|
||||
|
||||
for {
|
||||
var hdr *cpio.Header
|
||||
hdr, err = cin.Next()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mode := hdr.FileInfo().Mode()
|
||||
|
||||
if mode&os.ModeSymlink != 0 {
|
||||
// symlink target must be written after
|
||||
hdr.Size = int64(len(hdr.Linkname))
|
||||
}
|
||||
|
||||
err = cout.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if mode.IsRegular() {
|
||||
_, err = io.Copy(cout, cin)
|
||||
|
||||
} else if mode&os.ModeSymlink != 0 {
|
||||
_, err = cout.Write([]byte(hdr.Linkname))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range filesToAppend {
|
||||
err = func() (err error) {
|
||||
stat, err := os.Lstat(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
link := ""
|
||||
if stat.Mode()&os.ModeSymlink != 0 {
|
||||
link, err = os.Readlink(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hdr, err := cpio.FileInfoHeader(stat, link)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hdr.Name = file
|
||||
|
||||
cout.WriteHeader(hdr)
|
||||
|
||||
if stat.Mode().IsRegular() {
|
||||
var f *os.File
|
||||
f, err = os.Open(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(cout, f)
|
||||
|
||||
} else if stat.Mode()&os.ModeSymlink != 0 {
|
||||
_, err = cout.Write([]byte(link))
|
||||
}
|
||||
return
|
||||
}()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = cout.Close()
|
||||
return
|
||||
}
|
28
go.mod
28
go.mod
@ -1,17 +1,29 @@
|
||||
module novit.nc/direktil/initrd
|
||||
|
||||
require (
|
||||
github.com/cavaliergopher/cpio v1.0.1
|
||||
github.com/freddierice/go-losetup/v2 v2.0.1
|
||||
github.com/kr/pty v1.1.8
|
||||
github.com/pkg/term v1.1.0
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/crypto v0.16.0
|
||||
golang.org/x/sys v0.15.0
|
||||
golang.org/x/term v0.15.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
novit.nc/direktil/pkg v0.0.0-20191211161950-96b0448b84c2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
novit.tech/direktil/pkg v0.0.0-20231217121409-827fa62f58aa
|
||||
)
|
||||
|
||||
require github.com/creack/pty v1.1.17 // indirect
|
||||
require (
|
||||
github.com/cavaliergopher/cpio v1.0.1 // indirect
|
||||
github.com/creack/pty v1.1.21 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/socket v0.5.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
)
|
||||
|
||||
go 1.18
|
||||
go 1.21
|
||||
|
112
go.sum
112
go.sum
@ -1,8 +1,18 @@
|
||||
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
|
||||
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/freddierice/go-losetup/v2 v2.0.1 h1:wPDx/Elu9nDV8y/CvIbEDz5Xi5Zo80y4h7MKbi3XaAI=
|
||||
github.com/freddierice/go-losetup/v2 v2.0.1/go.mod h1:TEyBrvlOelsPEhfWD5rutNXDmUszBXuFnwT1kIQF4J8=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@ -10,23 +20,103 @@ github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mdlayher/genetlink v1.3.1 h1:roBiPnual+eqtRkKX2Jb8UQN5ZPWnhDCGj/wR6Jlz2w=
|
||||
github.com/mdlayher/genetlink v1.3.1/go.mod h1:uaIPxkWmGk753VVIzDtROxQ8+T+dkHqOI0vB1NA9S/Q=
|
||||
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
||||
github.com/mdlayher/netlink v1.7.1 h1:FdUaT/e33HjEXagwELR8R3/KL1Fq5x3G5jgHLp/BTmg=
|
||||
github.com/mdlayher/netlink v1.7.1/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
||||
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
|
||||
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
|
||||
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||
github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
|
||||
github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c h1:Okh6a1xpnJslG9Mn84pId1Mn+Q8cvpo4HCeeFWHo0cA=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb h1:c5tyN8sSp8jSDxdCCDXVOpJwYXXhmTkNMt+g0zTSOic=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
novit.nc/direktil/pkg v0.0.0-20191211161950-96b0448b84c2 h1:LN3K19gAJ1GamJXkzXAQmjbl8xCV7utqdxTTrM89MMc=
|
||||
novit.nc/direktil/pkg v0.0.0-20191211161950-96b0448b84c2/go.mod h1:zwTVO6U0tXFEaga73megQIBK7yVIKZJVePaIh/UtdfU=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
novit.nc/direktil/pkg v0.0.0-20220221171542-fd3ce3a1491b/go.mod h1:zwTVO6U0tXFEaga73megQIBK7yVIKZJVePaIh/UtdfU=
|
||||
novit.tech/direktil/pkg v0.0.0-20230201224712-5e39572dc50e h1:eQFbzcuB4wOSrnOhkcN30hFDCIack40VkIoqVRbWnWc=
|
||||
novit.tech/direktil/pkg v0.0.0-20230201224712-5e39572dc50e/go.mod h1:2Mir5x1eT/e295WeFGzzXa4siunKX4z+rmNPfVsXS0k=
|
||||
novit.tech/direktil/pkg v0.0.0-20231217121409-827fa62f58aa h1:eBk9nQTxIJU5cT8aJVjfRWiUd4sv8YV0kXALbSFOKdI=
|
||||
novit.tech/direktil/pkg v0.0.0-20231217121409-827fa62f58aa/go.mod h1:AYEEjNi7ljJG+V4F4LzxWntfbSs+KnNPO3kqvcEzIU4=
|
||||
|
26
losetup.go
Normal file
26
losetup.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/freddierice/go-losetup/v2"
|
||||
)
|
||||
|
||||
var loDevices = map[string]losetup.Device{}
|
||||
|
||||
func losetupAttach(file string) losetup.Device {
|
||||
if _, ok := loDevices[file]; !ok {
|
||||
dev, err := losetup.Attach(file, 0, true)
|
||||
if err != nil {
|
||||
fatalf("failed to attach %q to a loop device: %v", file, err)
|
||||
}
|
||||
|
||||
loDevices[file] = dev
|
||||
}
|
||||
return loDevices[file]
|
||||
}
|
||||
|
||||
func mountSquahfs(source, target string) {
|
||||
dev := losetupAttach(source)
|
||||
mount(dev.Path(), target, "squashfs", syscall.MS_RDONLY, "")
|
||||
}
|
154
lvm.go
154
lvm.go
@ -7,18 +7,32 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"novit.nc/direktil/initrd/config"
|
||||
"novit.nc/direktil/initrd/lvm"
|
||||
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||
)
|
||||
|
||||
func sortedKeys[T any](m map[string]T) (keys []string) {
|
||||
keys = make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return
|
||||
}
|
||||
|
||||
func setupLVM(cfg *config.Config) {
|
||||
if len(cfg.LVM) == 0 {
|
||||
log.Print("no LVM VG configured.")
|
||||
return
|
||||
}
|
||||
|
||||
// [dev] = filesystem
|
||||
// eg: [/dev/sda1] = ext4
|
||||
createdDevs := map[string]string{}
|
||||
|
||||
run("pvscan")
|
||||
run("vgscan", "--mknodes")
|
||||
|
||||
@ -27,17 +41,21 @@ func setupLVM(cfg *config.Config) {
|
||||
}
|
||||
|
||||
for _, vg := range cfg.LVM {
|
||||
setupLVs(vg)
|
||||
setupLVs(vg, createdDevs)
|
||||
}
|
||||
|
||||
run("vgchange", "--sysinit", "-a", "ly")
|
||||
|
||||
for _, vg := range cfg.LVM {
|
||||
setupCrypt(vg)
|
||||
}
|
||||
setupCrypt(cfg.Crypt, createdDevs)
|
||||
|
||||
for _, vg := range cfg.LVM {
|
||||
setupFS(vg)
|
||||
devs := make([]string, 0, len(createdDevs))
|
||||
for k := range createdDevs {
|
||||
devs = append(devs, k)
|
||||
}
|
||||
sort.Strings(devs)
|
||||
|
||||
for _, dev := range devs {
|
||||
setupFS(dev, createdDevs[dev])
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +131,7 @@ func setupVG(vg config.LvmVG) {
|
||||
}
|
||||
}
|
||||
|
||||
func setupLVs(vg config.LvmVG) {
|
||||
func setupLVs(vg config.LvmVG, createdDevs map[string]string) {
|
||||
lvsRep := lvm.LVSReport{}
|
||||
err := runJSON(&lvsRep, "lvs", "--reportformat", "json")
|
||||
if err != nil {
|
||||
@ -171,6 +189,12 @@ func setupLVs(vg config.LvmVG) {
|
||||
|
||||
dev := "/dev/" + vg.VG + "/" + lv.Name
|
||||
zeroDevStart(dev)
|
||||
|
||||
fs := lv.FS
|
||||
if fs == "" {
|
||||
fs = vg.Defaults.FS
|
||||
}
|
||||
createdDevs[dev] = fs
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,21 +212,52 @@ func zeroDevStart(dev string) {
|
||||
}
|
||||
}
|
||||
|
||||
func setupCrypt(vg config.LvmVG) {
|
||||
cryptDevs := map[string]bool{}
|
||||
var cryptDevs = map[string]bool{}
|
||||
|
||||
func setupCrypt(devSpecs []config.CryptDev, createdDevs map[string]string) {
|
||||
var password []byte
|
||||
passwordVerified := false
|
||||
|
||||
for _, lv := range vg.LVs {
|
||||
if lv.Crypt == "" {
|
||||
// flat, expanded devices to open
|
||||
devNames := make([]config.CryptDev, 0, len(devSpecs))
|
||||
|
||||
for _, devSpec := range devSpecs {
|
||||
if devSpec.Dev == "" && devSpec.Prefix == "" {
|
||||
fatalf("crypt: name %q: no dev or match set", devSpec.Name)
|
||||
}
|
||||
if devSpec.Dev != "" && devSpec.Prefix != "" {
|
||||
fatalf("crypt: name %q: both dev (%q) and match (%q) are set", devSpec.Name, devSpec.Dev, devSpec.Prefix)
|
||||
}
|
||||
|
||||
if devSpec.Dev != "" {
|
||||
// already flat
|
||||
devNames = append(devNames, devSpec)
|
||||
continue
|
||||
}
|
||||
|
||||
if cryptDevs[lv.Crypt] {
|
||||
fatalf("duplicate crypt device name: %s", lv.Crypt)
|
||||
matches, err := filepath.Glob(devSpec.Prefix + "*")
|
||||
if err != nil {
|
||||
fatalf("failed to search for device matches: %v", err)
|
||||
}
|
||||
cryptDevs[lv.Crypt] = true
|
||||
|
||||
for _, m := range matches {
|
||||
suffix := m[len(devSpec.Prefix):]
|
||||
|
||||
devNames = append(devNames, config.CryptDev{Dev: m, Name: devSpec.Name + suffix})
|
||||
}
|
||||
}
|
||||
|
||||
for _, devName := range devNames {
|
||||
name, dev := devName.Name, devName.Dev
|
||||
|
||||
if name == "" {
|
||||
name = filepath.Base(dev)
|
||||
}
|
||||
|
||||
if cryptDevs[name] {
|
||||
fatalf("duplicate crypt device name: %s", name)
|
||||
}
|
||||
cryptDevs[name] = true
|
||||
|
||||
retryOpen:
|
||||
if len(password) == 0 {
|
||||
@ -213,7 +268,10 @@ func setupCrypt(vg config.LvmVG) {
|
||||
}
|
||||
}
|
||||
|
||||
dev := "/dev/" + vg.VG + "/" + lv.Name
|
||||
fs := createdDevs[dev]
|
||||
delete(createdDevs, dev)
|
||||
|
||||
tgtDev := "/dev/mapper/" + name
|
||||
|
||||
needFormat := !devInitialized(dev)
|
||||
if needFormat {
|
||||
@ -242,10 +300,20 @@ func setupCrypt(vg config.LvmVG) {
|
||||
if err != nil {
|
||||
fatalf("failed luksFormat: %v", err)
|
||||
}
|
||||
|
||||
createdDevs[tgtDev] = fs
|
||||
}
|
||||
|
||||
log.Print("openning encrypted device ", lv.Crypt, " from ", dev)
|
||||
cmd := exec.Command("cryptsetup", "open", dev, lv.Crypt, "--key-file=-")
|
||||
if len(password) == 0 {
|
||||
password = askSecret("crypt password")
|
||||
|
||||
if len(password) == 0 {
|
||||
fatalf("empty password given")
|
||||
}
|
||||
}
|
||||
|
||||
log.Print("openning encrypted device ", name, " from ", dev)
|
||||
cmd := exec.Command("cryptsetup", "open", dev, name, "--key-file=-")
|
||||
cmd.Stdin = bytes.NewBuffer(password)
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
@ -261,7 +329,7 @@ func setupCrypt(vg config.LvmVG) {
|
||||
}
|
||||
|
||||
if needFormat {
|
||||
zeroDevStart("/dev/mapper/" + lv.Crypt)
|
||||
zeroDevStart(tgtDev)
|
||||
}
|
||||
|
||||
passwordVerified = true
|
||||
@ -294,33 +362,25 @@ func devInitialized(dev string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func setupFS(vg config.LvmVG) {
|
||||
for _, lv := range vg.LVs {
|
||||
dev := "/dev/" + vg.VG + "/" + lv.Name
|
||||
|
||||
if lv.Crypt != "" {
|
||||
dev = "/dev/mapper/" + lv.Crypt
|
||||
}
|
||||
|
||||
if devInitialized(dev) {
|
||||
log.Print("device ", dev, " already formatted")
|
||||
continue
|
||||
}
|
||||
|
||||
if lv.FS == "" {
|
||||
lv.FS = vg.Defaults.FS
|
||||
}
|
||||
|
||||
log.Print("formatting ", dev, " (", lv.FS, ")")
|
||||
args := make([]string, 0)
|
||||
|
||||
switch lv.FS {
|
||||
case "btrfs":
|
||||
args = append(args, "-f")
|
||||
case "ext4":
|
||||
args = append(args, "-F")
|
||||
}
|
||||
|
||||
run("mkfs."+lv.FS, append(args, dev)...)
|
||||
func setupFS(dev, fs string) {
|
||||
if devInitialized(dev) {
|
||||
log.Print("device ", dev, " already formatted")
|
||||
return
|
||||
}
|
||||
|
||||
if fs == "" {
|
||||
fs = "ext4"
|
||||
}
|
||||
|
||||
log.Print("formatting ", dev, " (", fs, ")")
|
||||
args := make([]string, 0)
|
||||
|
||||
switch fs {
|
||||
case "btrfs":
|
||||
args = append(args, "-f")
|
||||
case "ext4":
|
||||
args = append(args, "-F")
|
||||
}
|
||||
|
||||
run("mkfs."+fs, append(args, dev)...)
|
||||
}
|
||||
|
24
main.go
24
main.go
@ -41,8 +41,26 @@ func newPipe() (io.ReadCloser, io.WriteCloser) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
switch baseName := filepath.Base(os.Args[0]); baseName {
|
||||
case "init":
|
||||
runInit()
|
||||
default:
|
||||
log.Fatal("unknown sub-command: ", baseName)
|
||||
}
|
||||
}
|
||||
|
||||
func runInit() {
|
||||
if len(os.Args) > 1 && os.Args[1] == "hello" {
|
||||
fmt.Println("hello world!")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
runtime.LockOSThread()
|
||||
|
||||
if pid := os.Getpid(); pid != 1 {
|
||||
log.Fatal("init must be PID 1, not ", pid)
|
||||
}
|
||||
|
||||
// move log to shio
|
||||
go io.Copy(os.Stdout, stdout.NewReader())
|
||||
log.SetOutput(stderr)
|
||||
@ -134,8 +152,10 @@ mainLoop:
|
||||
|
||||
switch b[0] {
|
||||
case 'o':
|
||||
run("sync")
|
||||
syscall.Reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
|
||||
case 'r':
|
||||
run("sync")
|
||||
syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART)
|
||||
case 's':
|
||||
for _, sh := range []string{"bash", "ash", "sh", "busybox"} {
|
||||
@ -172,10 +192,6 @@ mainLoop:
|
||||
}
|
||||
}
|
||||
|
||||
func losetup(dev, file string) {
|
||||
run("/sbin/losetup", "-r", dev, file)
|
||||
}
|
||||
|
||||
func run(cmd string, args ...string) {
|
||||
if output, err := exec.Command(cmd, args...).CombinedOutput(); err != nil {
|
||||
fatalf("command %s %q failed: %v\n%s", cmd, args, err, string(output))
|
||||
|
17
modd.conf
17
modd.conf
@ -1,24 +1,13 @@
|
||||
modd.conf {}
|
||||
|
||||
alpine/Dockerfile {
|
||||
prep: docker build -t dkl-initrd alpine
|
||||
prep: docker run --rm dkl-initrd | base64 -d >dist/base-initrd
|
||||
}
|
||||
|
||||
buildenv/Dockerfile {
|
||||
prep: docker build -t dkl-initrd-build buildenv
|
||||
}
|
||||
|
||||
go.??? **/*.go {
|
||||
prep: go test ./...
|
||||
prep: mkdir -p dist
|
||||
prep: go build -o dist/ ./tools/...
|
||||
|
||||
prep: mkdir -p tmp/go tmp/.cache
|
||||
prep: docker run --rm -v novit-go:/go -e GOCACHE=/go/.cache -v $PWD:/src -u $(id -u):$(id -g) dkl-initrd-build go build -o dist/init .
|
||||
}
|
||||
|
||||
dist/init dist/base-initrd {
|
||||
prep: cd dist && ../dist/cpiocat init <base-initrd >initrd.new
|
||||
dist/init Dockerfile {
|
||||
prep: docker build -t novit-initrd-gen .
|
||||
prep: docker run novit-initrd-gen |base64 -d >dist/initrd.new
|
||||
prep: mv dist/initrd.new dist/initrd
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
modd.test.conf {}
|
||||
|
||||
dist/initrd dist/cpiocat test-initrd/* {
|
||||
dist/initrd dist/cpiocat dist/testconf test-initrd/**/* {
|
||||
prep: dist/testconf test-initrd/config.yaml
|
||||
prep: cp -f dist/initrd test-initrd.cpio
|
||||
prep: cd test-initrd && ../dist/cpiocat <../dist/initrd >../test-initrd.cpio *
|
||||
prep: if cpio -t -F test-initrd.cpio 2>&1 |grep bytes.of.junk; then echo "bad cpio archive"; exit 1; fi
|
||||
prep: cd test-initrd && cat ../dist/initrd | ../dist/cpiocat * |lz4 -l9v >../test-initrd.cpio.new
|
||||
prep: mv test-initrd.cpio.new test-initrd.cpio
|
||||
prep: if lz4cat test-initrd.cpio | cpio -t 2>&1 |grep bytes.of.junk; then echo "bad cpio archive"; exit 1; fi
|
||||
prep: kill $(<qemu.pid)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"novit.nc/direktil/initrd/config"
|
||||
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||
)
|
||||
|
||||
func setupNetworks(cfg *config.Config) {
|
||||
|
@ -1,12 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func param(name, defaultValue string) (value string) {
|
||||
ba, err := ioutil.ReadFile("/proc/cmdline")
|
||||
ba, err := os.ReadFile("/proc/cmdline")
|
||||
if err != nil {
|
||||
fatal("could not read /proc/cmdline: ", err)
|
||||
}
|
||||
|
56
ssh.go
56
ssh.go
@ -4,7 +4,6 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
@ -16,7 +15,7 @@ import (
|
||||
"github.com/kr/pty"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"novit.nc/direktil/initrd/config"
|
||||
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||
)
|
||||
|
||||
func startSSH(cfg *config.Config) {
|
||||
@ -24,18 +23,29 @@ func startSSH(cfg *config.Config) {
|
||||
PublicKeyCallback: sshCheckPubkey,
|
||||
}
|
||||
|
||||
pkBytes, err := ioutil.ReadFile("/id_rsa") // TODO configurable
|
||||
if err != nil {
|
||||
fatalf("ssh: failed to load private key: %v", err)
|
||||
hostKeyLoaded := false
|
||||
|
||||
for _, format := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
|
||||
pkBytes, err := os.ReadFile("/id_" + format)
|
||||
if err != nil {
|
||||
log.Printf("ssh : failed to load %s host key: %v", format, err)
|
||||
continue
|
||||
}
|
||||
|
||||
pk, err := ssh.ParsePrivateKey(pkBytes)
|
||||
if err != nil {
|
||||
log.Printf("ssh: failed to parse %s host key: %v", format, err)
|
||||
continue
|
||||
}
|
||||
|
||||
sshConfig.AddHostKey(pk)
|
||||
hostKeyLoaded = true
|
||||
}
|
||||
|
||||
pk, err := ssh.ParsePrivateKey(pkBytes)
|
||||
if err != nil {
|
||||
fatalf("ssh: failed to parse private key: %v", err)
|
||||
if !hostKeyLoaded {
|
||||
fatalf("ssh: failed to load any host key")
|
||||
}
|
||||
|
||||
sshConfig.AddHostKey(pk)
|
||||
|
||||
sshBind := ":22" // TODO configurable
|
||||
listener, err := net.Listen("tcp", sshBind)
|
||||
if err != nil {
|
||||
@ -118,7 +128,7 @@ func sshHandleChannel(remoteAddr string, channel ssh.Channel, requests <-chan *s
|
||||
}()
|
||||
|
||||
var once sync.Once
|
||||
close := func() {
|
||||
closeCh := func() {
|
||||
channel.Close()
|
||||
}
|
||||
|
||||
@ -130,11 +140,27 @@ func sshHandleChannel(remoteAddr string, channel ssh.Channel, requests <-chan *s
|
||||
case "init":
|
||||
go func() {
|
||||
io.Copy(channel, stdout.NewReader())
|
||||
once.Do(close)
|
||||
once.Do(closeCh)
|
||||
}()
|
||||
go func() {
|
||||
io.Copy(stdinPipe, channel)
|
||||
once.Do(close)
|
||||
once.Do(closeCh)
|
||||
}()
|
||||
|
||||
req.Reply(true, nil)
|
||||
|
||||
case "bootstrap":
|
||||
// extract a new bootstrap package
|
||||
os.MkdirAll("/bootstrap/current", 0750)
|
||||
|
||||
cmd := exec.Command("/bin/tar", "xv", "-C", "/bootstrap/current")
|
||||
cmd.Stdin = channel
|
||||
cmd.Stdout = channel
|
||||
cmd.Stderr = channel.Stderr()
|
||||
|
||||
go func() {
|
||||
cmd.Run()
|
||||
closeCh()
|
||||
}()
|
||||
|
||||
req.Reply(true, nil)
|
||||
@ -167,11 +193,11 @@ func sshHandleChannel(remoteAddr string, channel ssh.Channel, requests <-chan *s
|
||||
|
||||
go func() {
|
||||
io.Copy(channel, ptyF)
|
||||
once.Do(close)
|
||||
once.Do(closeCh)
|
||||
}()
|
||||
go func() {
|
||||
io.Copy(ptyF, channel)
|
||||
once.Do(close)
|
||||
once.Do(closeCh)
|
||||
}()
|
||||
|
||||
req.Reply(true, nil)
|
||||
|
@ -26,19 +26,27 @@ networks:
|
||||
- eno.*
|
||||
- enp.*
|
||||
script: |
|
||||
ip a add 2001:41d0:306:168f::1337:2eed/64 dev $iface
|
||||
ip li set $iface up
|
||||
udhcpc $iface
|
||||
#udhcpc $iface
|
||||
|
||||
pre_lvm_crypt:
|
||||
- dev: /dev/vda
|
||||
name: sys0
|
||||
- dev: /dev/vdb
|
||||
name: sys1
|
||||
|
||||
lvm:
|
||||
- vg: storage
|
||||
pvs:
|
||||
n: 2
|
||||
regexps:
|
||||
- /dev/mapper/sys[01]
|
||||
# to match full disks
|
||||
- /dev/nvme[0-9]+n[0-9]+
|
||||
- /dev/vd[a-z]+
|
||||
- /dev/sd[a-z]+
|
||||
- /dev/hd[a-z]+
|
||||
#- /dev/nvme[0-9]+n[0-9]+
|
||||
#- /dev/vd[a-z]+
|
||||
#- /dev/sd[a-z]+
|
||||
#- /dev/hd[a-z]+
|
||||
# to match partitions:
|
||||
#- /dev/nvme[0-9]+n[0-9]+p[0-9]+
|
||||
#- /dev/vd[a-z]+[0-9]+
|
||||
@ -52,18 +60,29 @@ lvm:
|
||||
|
||||
lvs:
|
||||
- name: bootstrap
|
||||
crypt: bootstrap
|
||||
size: 2g
|
||||
|
||||
- name: varlog
|
||||
crypt: varlog
|
||||
extents: 10%FREE
|
||||
# size: 10g
|
||||
|
||||
- name: podman
|
||||
extents: 10%FREE
|
||||
# size: 10g
|
||||
|
||||
- name: dls
|
||||
crypt: dls
|
||||
extents: 100%FREE
|
||||
# size: 10g
|
||||
|
||||
#crypt:
|
||||
#- dev: /dev/storage/bootstrap
|
||||
#- dev: /dev/storage/dls
|
||||
|
||||
bootstrap:
|
||||
dev: /dev/mapper/bootstrap
|
||||
#seed: https://direktil.novit.io/bootstraps/dls
|
||||
#dev: /dev/mapper/bootstrap
|
||||
dev: /dev/storage/bootstrap
|
||||
# TODO seed: https://direktil.novit.io/bootstraps/dls-crypt
|
||||
seed: http://192.168.10.254:7606/hosts/m1/bootstrap.tar
|
||||
# TODO seed_sign_key: "..."
|
||||
# TODO load_and_close: true
|
||||
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"novit.nc/direktil/initrd/cpiocat"
|
||||
"novit.tech/direktil/pkg/cpiocat"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
34
tools/testconf/main.go
Normal file
34
tools/testconf/main.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
for _, arg := range flag.Args() {
|
||||
log.Print("testing ", arg)
|
||||
|
||||
cfgBytes, err := os.ReadFile(arg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cfg := config.Config{}
|
||||
|
||||
dec := yaml.NewDecoder(bytes.NewBuffer(cfgBytes))
|
||||
dec.KnownFields(true)
|
||||
|
||||
err = dec.Decode(&cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
20
uname.go
Normal file
20
uname.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
|
||||
func unameRelease() string {
|
||||
uname := &syscall.Utsname{}
|
||||
if err := syscall.Uname(uname); err != nil {
|
||||
fatalf("failed to get kernel version: %v", err)
|
||||
}
|
||||
|
||||
ba := make([]byte, 0, len(uname.Release))
|
||||
for _, c := range uname.Release {
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
ba = append(ba, byte(c))
|
||||
}
|
||||
|
||||
return string(ba)
|
||||
}
|
125
vpn.go
Normal file
125
vpn.go
Normal file
@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"novit.tech/direktil/pkg/config"
|
||||
)
|
||||
|
||||
func setupVPN(vpn config.VPNDef, localGenDir string) {
|
||||
log.Printf("setting up VPN %s", vpn.Name)
|
||||
|
||||
vpnDir := filepath.Join(localGenDir, vpn.Name)
|
||||
os.MkdirAll(vpnDir, 0750)
|
||||
|
||||
// public/private key
|
||||
keyFile := filepath.Join(vpnDir, "key")
|
||||
keyBytes, err := os.ReadFile(keyFile)
|
||||
if os.IsNotExist(err) {
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
fatalf("failed to generate VPN key: %v", err)
|
||||
}
|
||||
|
||||
keyBytes = []byte(key.String())
|
||||
|
||||
os.WriteFile(keyFile, keyBytes, 0600)
|
||||
} else if err != nil {
|
||||
fatalf("failed to read VPN key: %v", err)
|
||||
}
|
||||
|
||||
key, err := wgtypes.ParseKey(string(keyBytes))
|
||||
if err != nil {
|
||||
fatalf("bad VPN key: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("VPN %s public key is %s", vpn.Name, key.PublicKey().String())
|
||||
|
||||
// pre-shared key
|
||||
pskeyFile := filepath.Join(vpnDir, "pskey")
|
||||
pskeyBytes, err := os.ReadFile(pskeyFile)
|
||||
if os.IsNotExist(err) {
|
||||
key, err := wgtypes.GenerateKey()
|
||||
if err != nil {
|
||||
fatalf("failed to generate VPN pre-shared key: %v", err)
|
||||
}
|
||||
|
||||
pskeyBytes = []byte(key.String())
|
||||
|
||||
os.WriteFile(pskeyFile, pskeyBytes, 0600)
|
||||
} else if err != nil {
|
||||
fatalf("failed to read VPN pre-shared key: %v", err)
|
||||
}
|
||||
|
||||
pskey, err := wgtypes.ParseKey(string(pskeyBytes))
|
||||
if err != nil {
|
||||
fatalf("bad VPN pre-shared key: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("VPN %s pre-shared key is %s", vpn.Name, key.String())
|
||||
|
||||
// setup interface
|
||||
cfg := wgtypes.Config{
|
||||
PrivateKey: &key,
|
||||
ListenPort: vpn.ListenPort,
|
||||
Peers: make([]wgtypes.PeerConfig, 0, len(vpn.Peers)),
|
||||
}
|
||||
|
||||
for idx, vpnPeer := range vpn.Peers {
|
||||
vpnPeer := vpnPeer
|
||||
|
||||
wgPeer := wgtypes.PeerConfig{
|
||||
Endpoint: vpnPeer.Endpoint,
|
||||
AllowedIPs: make([]net.IPNet, 0, len(vpnPeer.AllowedIPs)),
|
||||
|
||||
PersistentKeepaliveInterval: &vpnPeer.KeepAlive,
|
||||
}
|
||||
|
||||
if vpnPeer.WithPreSharedKey {
|
||||
wgPeer.PresharedKey = &pskey
|
||||
}
|
||||
|
||||
pubkey, err := wgtypes.ParseKey(vpnPeer.PublicKey)
|
||||
if err != nil {
|
||||
fatalf("bad VPN peer[%d] public key: %v", idx, err)
|
||||
}
|
||||
|
||||
wgPeer.PublicKey = pubkey
|
||||
|
||||
for _, ipnetStr := range vpnPeer.AllowedIPs {
|
||||
_, ipnet, err := net.ParseCIDR(ipnetStr)
|
||||
if err != nil {
|
||||
fatalf("bad IP/net: %q: %v", ipnetStr, err)
|
||||
}
|
||||
|
||||
wgPeer.AllowedIPs = append(wgPeer.AllowedIPs, *ipnet)
|
||||
}
|
||||
|
||||
cfg.Peers = append(cfg.Peers, wgPeer)
|
||||
}
|
||||
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
fatalf("failed to setup WireGuard client: %v", err)
|
||||
}
|
||||
defer wg.Close()
|
||||
|
||||
run("ip", "link", "add", vpn.Name, "type", "wireguard")
|
||||
|
||||
for _, ip := range vpn.IPs {
|
||||
run("ip", "addr", "add", ip, "dev", vpn.Name)
|
||||
}
|
||||
|
||||
err = wg.ConfigureDevice(vpn.Name, cfg)
|
||||
if err != nil {
|
||||
fatalf("failed to setup VPN %s: %v", vpn.Name, err)
|
||||
}
|
||||
|
||||
run("ip", "link", "set", vpn.Name, "up")
|
||||
}
|
Reference in New Issue
Block a user