Compare commits
5 Commits
8506f8807d
...
v2.1.1
Author | SHA1 | Date | |
---|---|---|---|
1555419549 | |||
3f2cd997a0 | |||
86d85f014c | |||
69cc01db9b | |||
3c7d56ae48 |
@ -1 +1,3 @@
|
|||||||
Dockerfile
|
Dockerfile
|
||||||
|
tmp/**/*
|
||||||
|
dist/*
|
||||||
|
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from golang:1.21.4-alpine3.18 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.18.4
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
entrypoint ["sh","-c","find |cpio -H newc -o |base64"]
|
@ -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 (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
@ -38,6 +39,8 @@ func askSecret(prompt string) []byte {
|
|||||||
fatalf("failed to read from stdin: %v", err)
|
fatalf("failed to read from stdin: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
s = bytes.TrimRight(s, "\r\n")
|
s = bytes.TrimRight(s, "\r\n")
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
3
auth.go
3
auth.go
@ -6,7 +6,8 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"novit.nc/direktil/initrd/config"
|
|
||||||
|
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
21
boot-v1.go
21
boot-v1.go
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -11,7 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
"novit.nc/direktil/pkg/sysfs"
|
"novit.tech/direktil/pkg/sysfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var loopOffset = 0
|
var loopOffset = 0
|
||||||
@ -58,7 +57,7 @@ func bootV1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
||||||
cfgBytes, err := ioutil.ReadFile(cfgPath)
|
cfgBytes, err := os.ReadFile(cfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf("failed to read %s: %v", cfgPath, err)
|
fatalf("failed to read %s: %v", cfgPath, err)
|
||||||
}
|
}
|
||||||
@ -134,7 +133,7 @@ func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
|||||||
|
|
||||||
// - write configuration
|
// - write configuration
|
||||||
log.Print("writing /config.yaml")
|
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)
|
fatal("failed: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +143,19 @@ func applyConfig(cfgPath string, bootMounted bool) (cfg *configV1) {
|
|||||||
|
|
||||||
filePath := filepath.Join("/system", fileDef.Path)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"novit.nc/direktil/initrd/config"
|
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func bootV2() {
|
func bootV2() {
|
||||||
|
60
bootstrap.go
60
bootstrap.go
@ -2,13 +2,15 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"novit.nc/direktil/initrd/config"
|
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func bootstrap(cfg *config.Config) {
|
func bootstrap(cfg *config.Config) {
|
||||||
@ -34,14 +36,58 @@ func bootstrap(cfg *config.Config) {
|
|||||||
|
|
||||||
log.Printf("seeding bootstrap from %s", seed)
|
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
|
layersDir = baseDir
|
||||||
layersOverride["modules"] = "/modules.sqfs"
|
layersOverride["modules"] = "/modules.sqfs"
|
||||||
sysCfg := applyConfig(sysCfgPath, false)
|
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 {
|
for _, mount := range sysCfg.Mounts {
|
||||||
log.Print("mount ", mount.Dev, " to system's ", mount.Path)
|
log.Print("mount ", mount.Dev, " to system's ", mount.Path)
|
||||||
|
|
||||||
@ -74,7 +120,7 @@ func bootstrap(cfg *config.Config) {
|
|||||||
func setUserPass(user, passwordHash string) {
|
func setUserPass(user, passwordHash string) {
|
||||||
const fpath = "/system/etc/shadow"
|
const fpath = "/system/etc/shadow"
|
||||||
|
|
||||||
ba, err := ioutil.ReadFile(fpath)
|
ba, err := os.ReadFile(fpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf("failed to read shadow: %v", err)
|
fatalf("failed to read shadow: %v", err)
|
||||||
}
|
}
|
||||||
@ -97,7 +143,7 @@ func setUserPass(user, passwordHash string) {
|
|||||||
buf.WriteByte('\n')
|
buf.WriteByte('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(fpath, buf.Bytes(), 0600)
|
err = os.WriteFile(fpath, buf.Bytes(), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf("failed to write shadow: %v", err)
|
fatalf("failed to write shadow: %v", err)
|
||||||
}
|
}
|
||||||
@ -116,7 +162,7 @@ func setAuthorizedKeys(ak []string) {
|
|||||||
fatalf("failed to create %s: %v", sshDir, err)
|
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 {
|
if err != nil {
|
||||||
fatalf("failed to write authorized keys: %v", err)
|
fatalf("failed to write authorized keys: %v", err)
|
||||||
}
|
}
|
||||||
|
23
config.go
23
config.go
@ -1,26 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
nconfig "novit.nc/direktil/pkg/config"
|
nconfig "novit.tech/direktil/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type configV1 struct {
|
type configV1 = nconfig.Config
|
||||||
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
|
|
||||||
}
|
|
||||||
|
@ -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
|
|
||||||
}
|
|
27
go.mod
27
go.mod
@ -1,17 +1,28 @@
|
|||||||
module novit.nc/direktil/initrd
|
module novit.nc/direktil/initrd
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cavaliergopher/cpio v1.0.1
|
|
||||||
github.com/kr/pty v1.1.8
|
github.com/kr/pty v1.1.8
|
||||||
github.com/pkg/term v1.1.0
|
github.com/pkg/term v1.1.0
|
||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
|
golang.org/x/crypto v0.5.0
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158
|
golang.org/x/sys v0.4.0
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
golang.org/x/term v0.4.0
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
novit.nc/direktil/pkg v0.0.0-20191211161950-96b0448b84c2
|
novit.tech/direktil/pkg v0.0.0-20230201224712-5e39572dc50e
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/creack/pty v1.1.17 // indirect
|
require (
|
||||||
|
github.com/cavaliergopher/cpio v1.0.1 // indirect
|
||||||
|
github.com/creack/pty v1.1.18 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
|
github.com/mdlayher/genetlink v1.3.1 // indirect
|
||||||
|
github.com/mdlayher/netlink v1.7.1 // indirect
|
||||||
|
github.com/mdlayher/socket v0.4.0 // indirect
|
||||||
|
golang.org/x/net v0.5.0 // indirect
|
||||||
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect
|
||||||
|
)
|
||||||
|
|
||||||
go 1.18
|
go 1.21
|
||||||
|
52
go.sum
52
go.sum
@ -1,8 +1,12 @@
|
|||||||
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
|
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
|
||||||
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
|
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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
|
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/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
@ -10,23 +14,47 @@ 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/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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
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/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/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
||||||
|
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||||
github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
|
github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
|
||||||
github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
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-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/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/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
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/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||||
|
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
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/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
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/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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.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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
novit.nc/direktil/pkg v0.0.0-20191211161950-96b0448b84c2 h1:LN3K19gAJ1GamJXkzXAQmjbl8xCV7utqdxTTrM89MMc=
|
novit.nc/direktil/pkg v0.0.0-20220221171542-fd3ce3a1491b/go.mod h1:zwTVO6U0tXFEaga73megQIBK7yVIKZJVePaIh/UtdfU=
|
||||||
novit.nc/direktil/pkg v0.0.0-20191211161950-96b0448b84c2/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=
|
||||||
|
156
lvm.go
156
lvm.go
@ -7,18 +7,32 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"novit.nc/direktil/initrd/config"
|
|
||||||
"novit.nc/direktil/initrd/lvm"
|
"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) {
|
func setupLVM(cfg *config.Config) {
|
||||||
if len(cfg.LVM) == 0 {
|
if len(cfg.LVM) == 0 {
|
||||||
log.Print("no LVM VG configured.")
|
log.Print("no LVM VG configured.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [dev] = filesystem
|
||||||
|
// eg: [/dev/sda1] = ext4
|
||||||
|
createdDevs := map[string]string{}
|
||||||
|
|
||||||
run("pvscan")
|
run("pvscan")
|
||||||
run("vgscan", "--mknodes")
|
run("vgscan", "--mknodes")
|
||||||
|
|
||||||
@ -27,17 +41,21 @@ func setupLVM(cfg *config.Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, vg := range cfg.LVM {
|
for _, vg := range cfg.LVM {
|
||||||
setupLVs(vg)
|
setupLVs(vg, createdDevs)
|
||||||
}
|
}
|
||||||
|
|
||||||
run("vgchange", "--sysinit", "-a", "ly")
|
run("vgchange", "--sysinit", "-a", "ly")
|
||||||
|
|
||||||
for _, vg := range cfg.LVM {
|
setupCrypt(cfg.Crypt, createdDevs)
|
||||||
setupCrypt(vg)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, vg := range cfg.LVM {
|
devs := make([]string, 0, len(createdDevs))
|
||||||
setupFS(vg)
|
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{}
|
lvsRep := lvm.LVSReport{}
|
||||||
err := runJSON(&lvsRep, "lvs", "--reportformat", "json")
|
err := runJSON(&lvsRep, "lvs", "--reportformat", "json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -171,6 +189,12 @@ func setupLVs(vg config.LvmVG) {
|
|||||||
|
|
||||||
dev := "/dev/" + vg.VG + "/" + lv.Name
|
dev := "/dev/" + vg.VG + "/" + lv.Name
|
||||||
zeroDevStart(dev)
|
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) {
|
func setupCrypt(devSpecs []config.CryptDev, createdDevs map[string]string) {
|
||||||
cryptDevs := map[string]bool{}
|
|
||||||
|
|
||||||
var password []byte
|
var password []byte
|
||||||
passwordVerified := false
|
passwordVerified := false
|
||||||
|
|
||||||
for _, lv := range vg.LVs {
|
// flat, expanded devices to open
|
||||||
if lv.Crypt == "" {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if cryptDevs[lv.Crypt] {
|
matches, err := filepath.Glob(devSpec.Prefix + "*")
|
||||||
fatalf("duplicate crypt device name: %s", lv.Crypt)
|
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})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptDevs := map[string]bool{}
|
||||||
|
|
||||||
|
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:
|
retryOpen:
|
||||||
if len(password) == 0 {
|
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)
|
needFormat := !devInitialized(dev)
|
||||||
if needFormat {
|
if needFormat {
|
||||||
@ -242,10 +300,20 @@ func setupCrypt(vg config.LvmVG) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf("failed luksFormat: %v", err)
|
fatalf("failed luksFormat: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createdDevs[tgtDev] = fs
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("openning encrypted device ", lv.Crypt, " from ", dev)
|
if len(password) == 0 {
|
||||||
cmd := exec.Command("cryptsetup", "open", dev, lv.Crypt, "--key-file=-")
|
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.Stdin = bytes.NewBuffer(password)
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
cmd.Stderr = stderr
|
cmd.Stderr = stderr
|
||||||
@ -261,7 +329,7 @@ func setupCrypt(vg config.LvmVG) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if needFormat {
|
if needFormat {
|
||||||
zeroDevStart("/dev/mapper/" + lv.Crypt)
|
zeroDevStart(tgtDev)
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordVerified = true
|
passwordVerified = true
|
||||||
@ -294,33 +362,25 @@ func devInitialized(dev string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupFS(vg config.LvmVG) {
|
func setupFS(dev, fs string) {
|
||||||
for _, lv := range vg.LVs {
|
if devInitialized(dev) {
|
||||||
dev := "/dev/" + vg.VG + "/" + lv.Name
|
log.Print("device ", dev, " already formatted")
|
||||||
|
return
|
||||||
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)...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)...)
|
||||||
}
|
}
|
||||||
|
20
main.go
20
main.go
@ -41,8 +41,26 @@ func newPipe() (io.ReadCloser, io.WriteCloser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
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()
|
runtime.LockOSThread()
|
||||||
|
|
||||||
|
if pid := os.Getpid(); pid != 1 {
|
||||||
|
log.Fatal("init must be PID 1, not ", pid)
|
||||||
|
}
|
||||||
|
|
||||||
// move log to shio
|
// move log to shio
|
||||||
go io.Copy(os.Stdout, stdout.NewReader())
|
go io.Copy(os.Stdout, stdout.NewReader())
|
||||||
log.SetOutput(stderr)
|
log.SetOutput(stderr)
|
||||||
@ -134,8 +152,10 @@ mainLoop:
|
|||||||
|
|
||||||
switch b[0] {
|
switch b[0] {
|
||||||
case 'o':
|
case 'o':
|
||||||
|
run("sync")
|
||||||
syscall.Reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
|
syscall.Reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
|
||||||
case 'r':
|
case 'r':
|
||||||
|
run("sync")
|
||||||
syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART)
|
syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART)
|
||||||
case 's':
|
case 's':
|
||||||
for _, sh := range []string{"bash", "ash", "sh", "busybox"} {
|
for _, sh := range []string{"bash", "ash", "sh", "busybox"} {
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"novit.nc/direktil/initrd/config"
|
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupNetworks(cfg *config.Config) {
|
func setupNetworks(cfg *config.Config) {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func param(name, defaultValue string) (value string) {
|
func param(name, defaultValue string) (value string) {
|
||||||
ba, err := ioutil.ReadFile("/proc/cmdline")
|
ba, err := os.ReadFile("/proc/cmdline")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("could not read /proc/cmdline: ", err)
|
fatal("could not read /proc/cmdline: ", err)
|
||||||
}
|
}
|
||||||
|
31
ssh.go
31
ssh.go
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@ -16,7 +15,7 @@ import (
|
|||||||
"github.com/kr/pty"
|
"github.com/kr/pty"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"novit.nc/direktil/initrd/config"
|
config "novit.tech/direktil/pkg/bootstrapconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startSSH(cfg *config.Config) {
|
func startSSH(cfg *config.Config) {
|
||||||
@ -24,7 +23,7 @@ func startSSH(cfg *config.Config) {
|
|||||||
PublicKeyCallback: sshCheckPubkey,
|
PublicKeyCallback: sshCheckPubkey,
|
||||||
}
|
}
|
||||||
|
|
||||||
pkBytes, err := ioutil.ReadFile("/id_rsa") // TODO configurable
|
pkBytes, err := os.ReadFile("/id_rsa") // TODO configurable
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf("ssh: failed to load private key: %v", err)
|
fatalf("ssh: failed to load private key: %v", err)
|
||||||
}
|
}
|
||||||
@ -118,7 +117,7 @@ func sshHandleChannel(remoteAddr string, channel ssh.Channel, requests <-chan *s
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
close := func() {
|
closeCh := func() {
|
||||||
channel.Close()
|
channel.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,11 +129,27 @@ func sshHandleChannel(remoteAddr string, channel ssh.Channel, requests <-chan *s
|
|||||||
case "init":
|
case "init":
|
||||||
go func() {
|
go func() {
|
||||||
io.Copy(channel, stdout.NewReader())
|
io.Copy(channel, stdout.NewReader())
|
||||||
once.Do(close)
|
once.Do(closeCh)
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
io.Copy(stdinPipe, channel)
|
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)
|
req.Reply(true, nil)
|
||||||
@ -167,11 +182,11 @@ func sshHandleChannel(remoteAddr string, channel ssh.Channel, requests <-chan *s
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
io.Copy(channel, ptyF)
|
io.Copy(channel, ptyF)
|
||||||
once.Do(close)
|
once.Do(closeCh)
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
io.Copy(ptyF, channel)
|
io.Copy(ptyF, channel)
|
||||||
once.Do(close)
|
once.Do(closeCh)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
req.Reply(true, nil)
|
req.Reply(true, nil)
|
||||||
|
@ -26,8 +26,9 @@ networks:
|
|||||||
- eno.*
|
- eno.*
|
||||||
- enp.*
|
- enp.*
|
||||||
script: |
|
script: |
|
||||||
|
ip a add 2001:41d0:306:168f::1337:2eed/64 dev $iface
|
||||||
ip li set $iface up
|
ip li set $iface up
|
||||||
udhcpc $iface
|
#udhcpc $iface
|
||||||
|
|
||||||
lvm:
|
lvm:
|
||||||
- vg: storage
|
- vg: storage
|
||||||
@ -52,18 +53,28 @@ lvm:
|
|||||||
|
|
||||||
lvs:
|
lvs:
|
||||||
- name: bootstrap
|
- name: bootstrap
|
||||||
crypt: bootstrap
|
|
||||||
size: 2g
|
size: 2g
|
||||||
|
|
||||||
- name: varlog
|
- name: varlog
|
||||||
crypt: varlog
|
|
||||||
extents: 10%FREE
|
extents: 10%FREE
|
||||||
|
# size: 10g
|
||||||
|
|
||||||
|
- name: podman
|
||||||
|
extents: 10%FREE
|
||||||
|
# size: 10g
|
||||||
|
|
||||||
- name: dls
|
- name: dls
|
||||||
crypt: dls
|
|
||||||
extents: 100%FREE
|
extents: 100%FREE
|
||||||
|
# size: 10g
|
||||||
|
|
||||||
|
crypt:
|
||||||
|
- dev: /dev/storage/bootstrap
|
||||||
|
- dev: /dev/storage/dls
|
||||||
|
|
||||||
bootstrap:
|
bootstrap:
|
||||||
dev: /dev/mapper/bootstrap
|
dev: /dev/mapper/bootstrap
|
||||||
#seed: https://direktil.novit.io/bootstraps/dls
|
# 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"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"novit.nc/direktil/initrd/cpiocat"
|
"novit.tech/direktil/pkg/cpiocat"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
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