Compare commits
8 Commits
dev
...
2af7ff85c1
| Author | SHA1 | Date | |
|---|---|---|---|
| 2af7ff85c1 | |||
| 512177cab0 | |||
| 26d3d0faeb | |||
| 834510760f | |||
| a2a970f93b | |||
| 350e753ae0 | |||
| 436be67bfd | |||
| 8ae52501c9 |
@ -1,6 +1,6 @@
|
||||
from novit.tech/direktil/dkl:bbea9b9 as dkl
|
||||
# ------------------------------------------------------------------------
|
||||
from golang:1.24.4-bookworm as build
|
||||
from golang:1.25.5-trixie as build
|
||||
|
||||
run apt-get update && apt-get install -y git
|
||||
|
||||
@ -22,13 +22,13 @@ run \
|
||||
hack/build ./...
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
from debian:bookworm
|
||||
from debian:trixie
|
||||
entrypoint ["/bin/dkl-local-server"]
|
||||
|
||||
env _uncache=1
|
||||
run apt-get update \
|
||||
&& yes |apt-get install -y genisoimage gdisk dosfstools util-linux udev binutils systemd \
|
||||
grub2 grub-pc-bin grub-efi-amd64-bin ca-certificates curl openssh-client qemu-utils \
|
||||
grub2 grub-pc-bin grub-efi-amd64-bin ca-certificates curl openssh-client qemu-utils wireguard-tools \
|
||||
&& apt-get clean
|
||||
|
||||
copy --from=dkl /bin/dkl /bin/dls /bin/
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
@ -127,12 +129,22 @@ func main() {
|
||||
|
||||
defer out.Close()
|
||||
|
||||
switch ext := path.Ext(*outPath); ext {
|
||||
case ".yaml":
|
||||
out.Write([]byte("# dkl-dir2config " + Version + "\n"))
|
||||
|
||||
if err = yaml.NewEncoder(out).Encode(dst); err != nil {
|
||||
log.Fatal("failed to render output: ", err)
|
||||
}
|
||||
|
||||
case ".json":
|
||||
if err = json.NewEncoder(out).Encode(dst); err != nil {
|
||||
log.Fatal("failed to render output: ", err)
|
||||
}
|
||||
|
||||
default:
|
||||
log.Fatal("unknown output file extension: ", ext)
|
||||
}
|
||||
}
|
||||
|
||||
func cfgPath(subPath string) string { return filepath.Join(*dir, subPath) }
|
||||
|
||||
@ -224,6 +224,7 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
reqJson := cleanJsonObject(buf.String())
|
||||
|
||||
key := name
|
||||
if req.PerHost {
|
||||
@ -236,11 +237,11 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
|
||||
dir := "/etc/tls/" + name
|
||||
|
||||
s = fmt.Sprintf("{{ %s %q %q %q %q %q %q %q }}", funcName,
|
||||
dir, cluster, req.CA, key, req.Profile, req.Label, buf.String())
|
||||
dir, cluster, req.CA, key, req.Profile, req.Label, reqJson)
|
||||
|
||||
default:
|
||||
s = fmt.Sprintf("{{ %s %q %q %q %q %q %q }}", funcName,
|
||||
cluster, req.CA, key, req.Profile, req.Label, buf.String())
|
||||
cluster, req.CA, key, req.Profile, req.Label, reqJson)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -281,11 +282,11 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
|
||||
},
|
||||
|
||||
"ssh_user_ca": func(path string) (s string) {
|
||||
return fmt.Sprintf("{{ ssh_user_ca %q %q}}",
|
||||
return fmt.Sprintf("{{ ssh_user_ca %q %q }}",
|
||||
path, cluster)
|
||||
},
|
||||
"ssh_host_keys": func(dir string) (s string) {
|
||||
return fmt.Sprintf("{{ ssh_host_keys %q %q \"\"}}",
|
||||
return fmt.Sprintf("{{ ssh_host_keys %q %q \"\" }}",
|
||||
dir, cluster)
|
||||
},
|
||||
"host_download_token": func() (s string) {
|
||||
|
||||
@ -29,7 +29,7 @@ func (ctx *renderContext) renderStaticPods() (pods []namePod) {
|
||||
for n := 0; ; n++ {
|
||||
str := buf.String()
|
||||
|
||||
podMap := map[string]interface{}{}
|
||||
podMap := map[string]any{}
|
||||
err := dec.Decode(podMap)
|
||||
|
||||
if err == io.EOF {
|
||||
@ -46,7 +46,7 @@ func (ctx *renderContext) renderStaticPods() (pods []namePod) {
|
||||
log.Fatalf("static pod %d: no metadata\n%s", n, buf.String())
|
||||
}
|
||||
|
||||
md := podMap["metadata"].(map[interface{}]interface{})
|
||||
md := podMap["metadata"].(map[any]any)
|
||||
|
||||
namespace := md["namespace"].(string)
|
||||
name := md["name"].(string)
|
||||
|
||||
15
cmd/dkl-dir2config/utils.go
Normal file
15
cmd/dkl-dir2config/utils.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func cleanJsonObject(raw string) string {
|
||||
v := map[string]any{}
|
||||
if err := json.Unmarshal([]byte(raw), &v); err != nil {
|
||||
panic(fmt.Errorf("invalid json: %w\n%s", err, raw))
|
||||
}
|
||||
clean, _ := json.Marshal(v)
|
||||
return string(clean)
|
||||
}
|
||||
@ -81,7 +81,7 @@ func buildInitrd(out io.Writer, ctx *renderContext) (err error) {
|
||||
cat.AppendDir("/etc/ssh", 0o700)
|
||||
|
||||
// XXX do we want bootstrap-stage keys instead of the real host key?
|
||||
for _, format := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
|
||||
for _, format := range []string{"rsa", "ecdsa", "ed25519"} {
|
||||
keyPath := "/etc/ssh/ssh_host_" + format + "_key"
|
||||
cat.AppendBytes(cfg.FileContent(keyPath), keyPath, 0o600)
|
||||
}
|
||||
|
||||
@ -60,6 +60,14 @@ func templateFuncs(sslCfg *cfsslconfig.Config) map[string]any {
|
||||
|
||||
return map[string]any{
|
||||
"quote": strconv.Quote,
|
||||
"yaml": asYaml,
|
||||
"indent": func(s, indent string) string {
|
||||
buf := new(strings.Builder)
|
||||
for _, line := range strings.Split(s, "\n") {
|
||||
buf.WriteString(indent + line + "\n")
|
||||
}
|
||||
return buf.String()
|
||||
},
|
||||
|
||||
"password": func(cluster, name, hashAlg string) (password string, err error) {
|
||||
key := cluster + "/" + name
|
||||
@ -170,40 +178,10 @@ func templateFuncs(sslCfg *cfsslconfig.Config) map[string]any {
|
||||
s = string(kc.Cert)
|
||||
return
|
||||
},
|
||||
|
||||
"tls_dir": func(dir, cluster, caName, name, profile, label, reqJson string) (s string, err error) {
|
||||
ca, err := getUsableClusterCA(cluster, caName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return asYaml([]config.FileDef{
|
||||
{
|
||||
Path: path.Join(dir, "ca.crt"),
|
||||
Mode: 0644,
|
||||
Content: string(ca.Cert),
|
||||
},
|
||||
{
|
||||
Path: path.Join(dir, "tls.crt"),
|
||||
Mode: 0644,
|
||||
Content: string(kc.Cert),
|
||||
},
|
||||
{
|
||||
Path: path.Join(dir, "tls.key"),
|
||||
Mode: 0600,
|
||||
Content: string(kc.Key),
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func asYaml(v interface{}) (string, error) {
|
||||
func asYaml(v any) (string, error) {
|
||||
ba, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@ -13,10 +14,12 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
cfsslconfig "github.com/cloudflare/cfssl/config"
|
||||
"github.com/cloudflare/cfssl/csr"
|
||||
restful "github.com/emicklei/go-restful"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
@ -165,6 +168,21 @@ func (ctx *renderContext) Tag() (string, error) {
|
||||
func (ctx *renderContext) TemplateFuncs() map[string]any {
|
||||
funcs := templateFuncs(ctx.SSLConfig)
|
||||
|
||||
// FIXME duplicate from cluster-render-context
|
||||
getKeyCert := func(cluster, caName, name, profile, label, reqJson string) (kc KeyCert, err error) {
|
||||
certReq := &csr.CertificateRequest{
|
||||
KeyRequest: csr.NewKeyRequest(),
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(reqJson), certReq)
|
||||
if err != nil {
|
||||
log.Print("CSR unmarshal failed on: ", reqJson)
|
||||
return
|
||||
}
|
||||
|
||||
return getUsableKeyCert(cluster, caName, name, profile, label, certReq, ctx.SSLConfig)
|
||||
}
|
||||
|
||||
for name, method := range map[string]any{
|
||||
"host_ip": func() (s string) {
|
||||
return ctx.Host.IPs[0]
|
||||
@ -177,6 +195,39 @@ func (ctx *renderContext) TemplateFuncs() map[string]any {
|
||||
return hex.EncodeToString(ba[:])
|
||||
},
|
||||
|
||||
"tls_dir": func(dir, cluster, caName, name, profile, label, reqJson string) (s string, err error) {
|
||||
ca, err := getUsableClusterCA(cluster, caName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
reqJson = strings.ReplaceAll(reqJson, "${host_ip}", ctx.Host.IPs[0])
|
||||
reqJson = strings.ReplaceAll(reqJson, "${host_name}", ctx.Host.Name)
|
||||
|
||||
kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return asYaml([]config.FileDef{
|
||||
{
|
||||
Path: path.Join(dir, "ca.crt"),
|
||||
Mode: 0644,
|
||||
Content: string(ca.Cert),
|
||||
},
|
||||
{
|
||||
Path: path.Join(dir, "tls.crt"),
|
||||
Mode: 0644,
|
||||
Content: string(kc.Cert),
|
||||
},
|
||||
{
|
||||
Path: path.Join(dir, "tls.key"),
|
||||
Mode: 0600,
|
||||
Content: string(kc.Key),
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
"ssh_user_ca": func(path, cluster string) (s string, err error) {
|
||||
userCA, err := sshCAPubKey(cluster)
|
||||
return asYaml([]config.FileDef{{
|
||||
@ -266,6 +317,20 @@ func (ctx *renderContext) TemplateFuncs() map[string]any {
|
||||
|
||||
return
|
||||
},
|
||||
"wg_key": func(name string) (key string, err error) {
|
||||
return wgKey(name + "/hosts/" + ctx.Host.Name)
|
||||
},
|
||||
"wg_psk": func(name, peerName string) (key string, err error) {
|
||||
a := ctx.Host.Name
|
||||
b := peerName
|
||||
if a > b {
|
||||
a, b = b, a
|
||||
}
|
||||
return wgKey(name + "/psks/" + a + " " + b)
|
||||
},
|
||||
"wg_pubkey": func(name, host string) (key string, err error) {
|
||||
return wgKey(name + "/hosts/" + host)
|
||||
},
|
||||
} {
|
||||
funcs[name] = method
|
||||
}
|
||||
|
||||
@ -32,7 +32,6 @@ func getSSHKeyPairs(host string) (pairs []SSHKeyPair, err error) {
|
||||
genLoop:
|
||||
for _, keyType := range []string{
|
||||
"rsa",
|
||||
"dsa",
|
||||
"ecdsa",
|
||||
"ed25519",
|
||||
} {
|
||||
@ -157,28 +156,25 @@ func sshCASign(cluster string, userPubKey []byte, principal, validity string, op
|
||||
return
|
||||
}
|
||||
|
||||
_, identity, _, _, err := ssh.ParseAuthorizedKey(userPubKey)
|
||||
pubkey, identity, _, _, err := ssh.ParseAuthorizedKey(bytes.TrimSpace(userPubKey))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ak := ssh.MarshalAuthorizedKey(pubkey)
|
||||
|
||||
userPubKeyFile, err := os.CreateTemp("/tmp", "user.pub")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.Remove(userPubKeyFile.Name())
|
||||
|
||||
_, err = io.Copy(userPubKeyFile, bytes.NewBuffer(userPubKey))
|
||||
_, err = io.Copy(userPubKeyFile, bytes.NewBuffer(ak))
|
||||
userPubKeyFile.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile(userPubKeyFile.Name(), userPubKey, 0600)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
serial := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
cmd := exec.Command("ssh-keygen", "-q", "-s", "/dev/stdin", "-I", identity, "-z", serial, "-n", principal)
|
||||
|
||||
|
||||
44
cmd/dkl-local-server/wireguard.go
Normal file
44
cmd/dkl-local-server/wireguard.go
Normal file
@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
var wgKeys = KVSecrets[string]{"wireguard"}
|
||||
|
||||
func wgKey(path string) (key string, err error) {
|
||||
return wgKeys.GetOrCreate(path, func() (key string, err error) {
|
||||
k, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
key = k.String()
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func wgPSKey(path string) (key string, err error) {
|
||||
return wgKeys.GetOrCreate(path, func() (key string, err error) {
|
||||
k, err := wgtypes.GenerateKey()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
key = k.String()
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func wgPubKey(path string) (pubkey string, err error) {
|
||||
key, err := wgKey(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
k, err := wgtypes.ParseKey(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pubkey = k.PublicKey().String()
|
||||
return
|
||||
}
|
||||
@ -1,20 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
restful "github.com/emicklei/go-restful"
|
||||
"gopkg.in/yaml.v2"
|
||||
"m.cluseau.fr/go/httperr"
|
||||
"novit.tech/direktil/pkg/localconfig"
|
||||
)
|
||||
|
||||
func wsUploadConfig(req *restful.Request, resp *restful.Response) {
|
||||
body := req.Request.Body
|
||||
cfg := &localconfig.Config{}
|
||||
if err := req.ReadEntity(cfg); err != nil {
|
||||
wsError(resp, httperr.BadRequest(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
err := writeNewConfig(body)
|
||||
body.Close()
|
||||
cfgBytes, err := yaml.Marshal(cfg)
|
||||
if err != nil {
|
||||
wsError(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = writeNewConfig(cfgBytes)
|
||||
if err != nil {
|
||||
wsError(resp, err)
|
||||
return
|
||||
@ -23,7 +35,7 @@ func wsUploadConfig(req *restful.Request, resp *restful.Response) {
|
||||
resp.WriteEntity(true)
|
||||
}
|
||||
|
||||
func writeNewConfig(reader io.Reader) (err error) {
|
||||
func writeNewConfig(cfgBytes []byte) (err error) {
|
||||
out, err := os.CreateTemp(*dataDir, ".config-upload")
|
||||
if err != nil {
|
||||
return
|
||||
@ -31,7 +43,7 @@ func writeNewConfig(reader io.Reader) (err error) {
|
||||
|
||||
defer os.Remove(out.Name())
|
||||
|
||||
_, err = io.Copy(out, reader)
|
||||
_, err = io.Copy(out, bytes.NewReader(cfgBytes))
|
||||
out.Close()
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
|
||||
cfsslconfig "github.com/cloudflare/cfssl/config"
|
||||
"github.com/emicklei/go-restful"
|
||||
"gopkg.in/yaml.v2"
|
||||
"m.cluseau.fr/go/httperr"
|
||||
|
||||
"novit.tech/direktil/pkg/localconfig"
|
||||
@ -75,7 +76,7 @@ func registerWS(rest *restful.Container) {
|
||||
|
||||
// - configs API
|
||||
ws.Route(ws.POST("/configs").To(wsUploadConfig).
|
||||
Consumes(mime.YAML).Param(ws.BodyParameter("config", "The new full configuration")).
|
||||
Consumes(mime.YAML, mime.JSON).Param(ws.BodyParameter("config", "The new full configuration")).
|
||||
Produces(mime.JSON).Writes(true).
|
||||
Doc("Upload a new current configuration, archiving the previous one"))
|
||||
|
||||
@ -307,3 +308,26 @@ func wsRender(resp *restful.Response, sslCfg *cfsslconfig.Config, tmplStr string
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
restful.RegisterEntityAccessor(mime.YAML, yamlEntityAccessor{})
|
||||
}
|
||||
|
||||
type yamlEntityAccessor struct{}
|
||||
|
||||
var _ restful.EntityReaderWriter = yamlEntityAccessor{}
|
||||
|
||||
func (_ yamlEntityAccessor) Read(req *restful.Request, v any) error {
|
||||
decoder := yaml.NewDecoder(req.Request.Body)
|
||||
return decoder.Decode(v)
|
||||
}
|
||||
func (_ yamlEntityAccessor) Write(resp *restful.Response, status int, v any) error {
|
||||
if v == nil {
|
||||
resp.WriteHeader(status)
|
||||
// do not write a nil representation
|
||||
return nil
|
||||
}
|
||||
resp.Header().Set("Content-Type", mime.YAML)
|
||||
resp.WriteHeader(status)
|
||||
return yaml.NewEncoder(resp.ResponseWriter).Encode(v)
|
||||
}
|
||||
|
||||
5
go.mod
5
go.mod
@ -11,12 +11,15 @@ require (
|
||||
github.com/emicklei/go-restful v2.16.0+incompatible
|
||||
github.com/emicklei/go-restful-openapi v1.4.1
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/mcluseau/go-swagger-ui v0.0.0-20191019002626-fd9128c24a34
|
||||
github.com/miolini/datacounter v1.0.3
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible
|
||||
github.com/sergeymakinen/go-crypt v1.0.1
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@ -55,7 +58,6 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/kisielk/sqlstruct v0.0.0-20210630145711-dae28ed37023 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@ -71,7 +73,6 @@ require (
|
||||
github.com/zmap/zlint/v3 v3.5.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@ -3,8 +3,6 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
||||
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
@ -313,6 +311,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
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/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
||||
@ -15,8 +15,8 @@ export default {
|
||||
sshUserCert: null,
|
||||
kubeSignReq: {
|
||||
CSR: "",
|
||||
User: "anonymous",
|
||||
Group: "",
|
||||
User: "",
|
||||
Group: "system:masters",
|
||||
},
|
||||
kubeUserCert: null,
|
||||
};
|
||||
@ -28,8 +28,13 @@ export default {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ...this.sshSignReq, Validity: this.signReqValidity }),
|
||||
headers: { 'Authorization': 'Bearer ' + this.token, 'Content-Type': 'application/json' },
|
||||
}).then((resp) => resp.blob())
|
||||
.then((cert) => { this.sshUserCert = URL.createObjectURL(cert) })
|
||||
}).then((resp) => {
|
||||
if (resp.ok) {
|
||||
resp.blob().then((cert) => { this.sshUserCert = URL.createObjectURL(cert) })
|
||||
} else {
|
||||
resp.json().then((resp) => alert('failed to sign: '+resp.message))
|
||||
}
|
||||
})
|
||||
.catch((e) => { alert('failed to sign: '+e); })
|
||||
},
|
||||
kubeCASign() {
|
||||
@ -38,8 +43,13 @@ export default {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ...this.kubeSignReq, Validity: this.signReqValidity }),
|
||||
headers: { 'Authorization': 'Bearer ' + this.token, 'Content-Type': 'application/json' },
|
||||
}).then((resp) => resp.blob())
|
||||
.then((cert) => { this.kubeUserCert = URL.createObjectURL(cert) })
|
||||
}).then((resp) => {
|
||||
if (resp.ok) {
|
||||
resp.blob().then((cert) => { this.kubeUserCert = URL.createObjectURL(cert) })
|
||||
} else {
|
||||
resp.json().then((resp) => alert('failed to sign: '+resp.message))
|
||||
}
|
||||
})
|
||||
.catch((e) => { alert('failed to sign: '+e); })
|
||||
},
|
||||
},
|
||||
@ -78,11 +88,12 @@ export default {
|
||||
<p>Public key (OpenSSH format):<br/>
|
||||
<textarea v-model="sshSignReq.PubKey" style="width:64em;height:2lh"></textarea>
|
||||
</p>
|
||||
<p>Principal: <input type="text" v-model="sshSignReq.Principal"/></p>
|
||||
<p>User: <input type="text" v-model="sshSignReq.Principal"/></p>
|
||||
|
||||
<p><button @click="sshCASign">Sign SSH access request</button></p>
|
||||
<p v-if="sshUserCert">
|
||||
<a :href="sshUserCert" download="ssh-cert.pub">Get certificate</a>
|
||||
<p><button @click="sshCASign">Sign SSH access (validity: {{signReqValidity}})</button>
|
||||
<template v-if="sshUserCert">
|
||||
=> <a :href="sshUserCert" download="ssh-cert.pub">Get certificate</a>
|
||||
</template>
|
||||
</p>
|
||||
|
||||
<h4>Grant Kubernetes API access</h4>
|
||||
@ -93,9 +104,10 @@ export default {
|
||||
<p>User: <input type="text" v-model="kubeSignReq.User"/></p>
|
||||
<p>Group: <input type="text" v-model="kubeSignReq.Group"/></p>
|
||||
|
||||
<p><button @click="kubeCASign">Sign Kubernetes API access request</button></p>
|
||||
<p v-if="kubeUserCert">
|
||||
<a :href="kubeUserCert" download="kube-cert.pub">Get certificate</a>
|
||||
<p><button @click="kubeCASign">Sign Kubernetes API access (validity: {{signReqValidity}})</button>
|
||||
<template v-if="kubeUserCert">
|
||||
=> <a :href="kubeUserCert" download="kube-cert.pub">Get certificate</a>
|
||||
</template>
|
||||
</p>
|
||||
`
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ createApp({
|
||||
(this.state.Clusters||[]).forEach((c) => views.push({type: "cluster", name: c.Name, title: `Cluster ${c.Name}`}));
|
||||
(this.state.Hosts ||[]).forEach((c) => views.push({type: "host", name: c.Name, title: `Host ${c.Name}`}));
|
||||
|
||||
return views.filter((v) => v.name.includes(this.viewFilter));
|
||||
return views.filter((v) => v.type != "host" || v.name.includes(this.viewFilter));
|
||||
},
|
||||
viewObj() {
|
||||
if (this.view) {
|
||||
|
||||
@ -1,37 +1,23 @@
|
||||
#! /bin/sh
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "USAGE: $0 <device> <base url>"
|
||||
echo "USAGE: $0 <device>"
|
||||
fi
|
||||
|
||||
dev=$1
|
||||
base_url=$2
|
||||
|
||||
: ${MP:=/mnt}
|
||||
|
||||
set -ex
|
||||
|
||||
mkdir -p $MP
|
||||
zcat boot.img.gz | dd of=$dev
|
||||
|
||||
apk add sgdisk
|
||||
|
||||
[[ $dev =~ nvme ]] &&
|
||||
devp=${dev}p ||
|
||||
devp=${dev}
|
||||
|
||||
if vgdisplay storage; then
|
||||
# the system is already installed, just upgrade
|
||||
mount -t vfat ${devp}1 $MP
|
||||
curl ${base_url}/boot.tar |tar xv -C $MP
|
||||
umount $MP
|
||||
sgdisk --move-second-header --new=3:0:0 $dev
|
||||
|
||||
else
|
||||
sgdisk --clear $dev
|
||||
pvcreate ${devp}3
|
||||
vgcreate storage ${devp}3
|
||||
|
||||
curl ${base_url}/boot.img.lz4 |lz4cat >$dev
|
||||
|
||||
sgdisk --move-second-header --new=3:0:0 $dev
|
||||
|
||||
pvcreate ${devp}3
|
||||
vgcreate storage ${devp}3
|
||||
fi
|
||||
|
||||
while umount $MP; do true; done
|
||||
|
||||
@ -2,7 +2,6 @@ package clustersconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@ -178,7 +177,7 @@ type dirStore struct {
|
||||
|
||||
// listDir
|
||||
func (b *dirStore) listDir(prefix string) (subDirs []string, err error) {
|
||||
entries, err := ioutil.ReadDir(filepath.Join(b.path, prefix))
|
||||
entries, err := os.ReadDir(filepath.Join(b.path, prefix))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -226,7 +225,7 @@ func (b *dirStore) List(prefix string) ([]string, error) {
|
||||
|
||||
// Load is part of the DataBackend interface
|
||||
func (b *dirStore) Get(key string) (ba []byte, err error) {
|
||||
ba, err = ioutil.ReadFile(filepath.Join(b.path, filepath.Join(path.Split(key))+".yaml"))
|
||||
ba, err = os.ReadFile(filepath.Join(b.path, filepath.Join(path.Split(key))+".yaml"))
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user