Compare commits
28 Commits
4b05458cec
...
dev
Author | SHA1 | Date | |
---|---|---|---|
98eb601fd3 | |||
8e87d406e4 | |||
f83b1eab23 | |||
d03a7ab4ec | |||
cd69d9234e | |||
5fa367949b | |||
cef4441208 | |||
d4087d3534 | |||
ab6f0b6358 | |||
af2758dead | |||
899a0a9dab | |||
08cbccc756 | |||
62882e78d8 | |||
47843f202f | |||
216236c1eb | |||
6651ff0364 | |||
d77588274b | |||
20b6769cbb | |||
9ad7715a29 | |||
5c91736202 | |||
73c533116c | |||
c8759e03d5 | |||
f5abdfdf3f | |||
7a55735cc4 | |||
48201132bd | |||
37713f8c16 | |||
85b9a45856 | |||
af41df6ab4 |
@ -1,5 +1,6 @@
|
|||||||
|
from novit.tech/direktil/dkl:bbea9b9 as dkl
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
from golang:1.24.3-bookworm as build
|
from golang:1.24.4-bookworm as build
|
||||||
|
|
||||||
run apt-get update && apt-get install -y git
|
run apt-get update && apt-get install -y git
|
||||||
|
|
||||||
@ -30,4 +31,5 @@ run apt-get update \
|
|||||||
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 \
|
||||||
&& apt-get clean
|
&& apt-get clean
|
||||||
|
|
||||||
|
copy --from=dkl /bin/dkl /bin/dls /bin/
|
||||||
copy --from=build /src/dist/ /bin/
|
copy --from=build /src/dist/ /bin/
|
||||||
|
@ -124,7 +124,7 @@ func eachFragment(path string, searchList []FS, walk func(io.Reader) error) (err
|
|||||||
log.Print("#!gen ", cmdArgs)
|
log.Print("#!gen ", cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := "gen/" + cmdArgs[0]
|
cmd := *dir + "/gen/" + cmdArgs[0]
|
||||||
args := cmdArgs[1:]
|
args := cmdArgs[1:]
|
||||||
genOutput, err := exec.Command(cmd, args...).Output()
|
genOutput, err := exec.Command(cmd, args...).Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash"
|
||||||
@ -203,7 +204,7 @@ func (ctx *renderContext) renderConfigTo(buf io.Writer, configTemplate *clusters
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]interface{} {
|
func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
|
||||||
cluster := ctx.Cluster.Name
|
cluster := ctx.Cluster.Name
|
||||||
|
|
||||||
getKeyCert := func(name, funcName string) (s string, err error) {
|
getKeyCert := func(name, funcName string) (s string, err error) {
|
||||||
@ -229,14 +230,15 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]interf
|
|||||||
key += "/" + ctx.Host.Name
|
key += "/" + ctx.Host.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if funcName == "tls_dir" {
|
switch funcName {
|
||||||
|
case "tls_dir":
|
||||||
// needs the dir name
|
// needs the dir name
|
||||||
dir := "/etc/tls/" + name
|
dir := "/etc/tls/" + name
|
||||||
|
|
||||||
s = fmt.Sprintf("{{ %s %q %q %q %q %q %q %q }}", funcName,
|
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, buf.String())
|
||||||
|
|
||||||
} else {
|
default:
|
||||||
s = fmt.Sprintf("{{ %s %q %q %q %q %q %q }}", funcName,
|
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, buf.String())
|
||||||
}
|
}
|
||||||
@ -266,6 +268,9 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]interf
|
|||||||
"tls_key": func(name string) (string, error) {
|
"tls_key": func(name string) (string, error) {
|
||||||
return getKeyCert(name, "tls_key")
|
return getKeyCert(name, "tls_key")
|
||||||
},
|
},
|
||||||
|
"tls_pubkey": func(name string) string {
|
||||||
|
return fmt.Sprintf("{{ tls_pubkey %q %q }}", ctx.Cluster.Name, name)
|
||||||
|
},
|
||||||
|
|
||||||
"tls_crt": func(name string) (s string, err error) {
|
"tls_crt": func(name string) (s string, err error) {
|
||||||
return getKeyCert(name, "tls_crt")
|
return getKeyCert(name, "tls_crt")
|
||||||
@ -275,6 +280,10 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]interf
|
|||||||
return getKeyCert(name, "tls_dir")
|
return getKeyCert(name, "tls_dir")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ssh_user_ca": func(path string) (s string) {
|
||||||
|
return fmt.Sprintf("{{ ssh_user_ca %q %q}}",
|
||||||
|
path, cluster)
|
||||||
|
},
|
||||||
"ssh_host_keys": func(dir string) (s string) {
|
"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)
|
dir, cluster)
|
||||||
@ -282,6 +291,14 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]interf
|
|||||||
"host_download_token": func() (s string) {
|
"host_download_token": func() (s string) {
|
||||||
return "{{ host_download_token }}"
|
return "{{ host_download_token }}"
|
||||||
},
|
},
|
||||||
|
"asset_download_token": func(args ...string) (s string) {
|
||||||
|
argsStr := new(strings.Builder)
|
||||||
|
for _, arg := range args {
|
||||||
|
argsStr.WriteByte(' ')
|
||||||
|
argsStr.WriteString(strconv.Quote(arg))
|
||||||
|
}
|
||||||
|
return "{{ asset_download_token" + argsStr.String() + " }}"
|
||||||
|
},
|
||||||
|
|
||||||
"hosts_of_group": func() (hosts []any) {
|
"hosts_of_group": func() (hosts []any) {
|
||||||
hosts = make([]any, 0)
|
hosts = make([]any, 0)
|
||||||
|
@ -83,7 +83,9 @@ func buildBootImgQemuConvert(out io.Writer, ctx *renderContext, format string) (
|
|||||||
return
|
return
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if imgPath != "" {
|
||||||
defer os.Remove(imgPath)
|
defer os.Remove(imgPath)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -107,7 +109,7 @@ func qemuImgBootImg(format string) func(out io.Writer, ctx *renderContext) (err
|
|||||||
var grubSupportVersion = flag.String("grub-support", "1.1.0", "GRUB support version")
|
var grubSupportVersion = flag.String("grub-support", "1.1.0", "GRUB support version")
|
||||||
|
|
||||||
func setupBootImage(bootImg *os.File, ctx *renderContext) (err error) {
|
func setupBootImage(bootImg *os.File, ctx *renderContext) (err error) {
|
||||||
path, err := ctx.distFetch("grub-support", *grubSupportVersion)
|
path, err := distFetch("grub-support", *grubSupportVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -146,6 +148,7 @@ func setupBootImage(bootImg *os.File, ctx *renderContext) (err error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
log.Print("device: ", dev)
|
log.Print("device: ", dev)
|
||||||
|
syncSysToDev()
|
||||||
|
|
||||||
tempDir := bootImg.Name() + ".p1.mount"
|
tempDir := bootImg.Name() + ".p1.mount"
|
||||||
|
|
||||||
@ -159,9 +162,10 @@ func setupBootImage(bootImg *os.File, ctx *renderContext) (err error) {
|
|||||||
os.RemoveAll(tempDir)
|
os.RemoveAll(tempDir)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = syscall.Mount(dev+"p1", tempDir, "vfat", 0, "")
|
devp1 := dev + "p1"
|
||||||
|
err = syscall.Mount(devp1, tempDir, "vfat", 0, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to mount %s to %s: %v", dev+"p1", tempDir, err)
|
return fmt.Errorf("failed to mount %s to %s: %v", devp1, tempDir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -31,7 +31,7 @@ func buildBootTar(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// kernel
|
// kernel
|
||||||
kernelPath, err := ctx.distFetch("kernels", ctx.Host.Kernel)
|
kernelPath, err := distFetch("kernels", ctx.Host.Kernel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ func buildBootEFITar(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// kernel
|
// kernel
|
||||||
kernelPath, err := ctx.distFetch("kernels", ctx.Host.Kernel)
|
kernelPath, err := distFetch("kernels", ctx.Host.Kernel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -9,6 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"novit.tech/direktil/pkg/cpiocat"
|
"novit.tech/direktil/pkg/cpiocat"
|
||||||
@ -37,10 +40,15 @@ func buildInitrd(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cat := cpiocat.New(out)
|
zout, err := zstd.NewWriter(out, zstd.WithEncoderLevel(zstd.EncoderLevelFromZstd(12)))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("zstd writer setup failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cat := cpiocat.New(zout)
|
||||||
|
|
||||||
// initrd
|
// initrd
|
||||||
initrdPath, err := ctx.distFetch("initrd", ctx.Host.Initrd)
|
initrdPath, err := distFetch("initrd", ctx.Host.Initrd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -52,7 +60,7 @@ func buildInitrd(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
case "modules":
|
case "modules":
|
||||||
|
|
||||||
layerVersion := ctx.Host.Versions[layer]
|
layerVersion := ctx.Host.Versions[layer]
|
||||||
modulesPath, err := ctx.distFetch("layers", layer, layerVersion)
|
modulesPath, err := distFetch("layers", layer, layerVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -66,28 +74,82 @@ func buildInitrd(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cat.AppendBytes(cfgBytes, "config.yaml", 0600)
|
cat.AppendBytes(cfgBytes, "config.yaml", 0o600)
|
||||||
|
|
||||||
// ssh keys
|
// ssh keys
|
||||||
// FIXME we want a bootstrap-stage key instead of the real host key
|
cat.AppendDir("/etc", 0o755)
|
||||||
|
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", "dsa", "ecdsa", "ed25519"} {
|
||||||
cat.AppendBytes(cfg.FileContent("/etc/ssh/ssh_host_"+format+"_key"), "id_"+format, 0600)
|
keyPath := "/etc/ssh/ssh_host_" + format + "_key"
|
||||||
|
cat.AppendBytes(cfg.FileContent(keyPath), keyPath, 0o600)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cat.Close()
|
// ssh user CA
|
||||||
|
userCA, err := sshCAPubKey(ctx.Host.ClusterName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get SSH user CA: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cat.AppendBytes(userCA, "user_ca.pub", 0600)
|
||||||
|
|
||||||
|
if err = cat.Close(); err != nil {
|
||||||
|
return fmt.Errorf("cpio close failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = zout.Close(); err != nil {
|
||||||
|
return fmt.Errorf("zstd close failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildBootstrap(out io.Writer, ctx *renderContext) (err error) {
|
func buildBootstrap(out io.Writer, ctx *renderContext) (err error) {
|
||||||
arch := tar.NewWriter(out)
|
arch := tar.NewWriter(out)
|
||||||
defer arch.Close()
|
defer arch.Close()
|
||||||
|
|
||||||
|
ca, err := getUsableClusterCA(ctx.Host.ClusterName, "boot-signer")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
signer, err := ca.ParseKey()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := crypto.SHA512
|
||||||
|
|
||||||
|
sign := func(name string, digest []byte) (err error) {
|
||||||
|
sigBytes, err := signer.Sign(nil, digest, hash)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("signing to %s failed: %w", name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = arch.WriteHeader(&tar.Header{
|
||||||
|
Name: name,
|
||||||
|
Size: int64(len(sigBytes)),
|
||||||
|
Mode: 0o644,
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(arch, bytes.NewReader(sigBytes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// config
|
// config
|
||||||
cfgBytes, cfg, err := ctx.Config()
|
cfgBytes, cfg, err := ctx.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = arch.WriteHeader(&tar.Header{Name: "config.yaml", Size: int64(len(cfgBytes))})
|
err = arch.WriteHeader(&tar.Header{
|
||||||
|
Name: "config.yaml",
|
||||||
|
Size: int64(len(cfgBytes)),
|
||||||
|
Mode: 0o600,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -97,10 +159,19 @@ func buildBootstrap(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
h := hash.New()
|
||||||
|
h.Write(cfgBytes)
|
||||||
|
err = sign("config.yaml.sig", h.Sum(nil))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// layers
|
// layers
|
||||||
for _, layer := range cfg.Layers {
|
for _, layer := range cfg.Layers {
|
||||||
if layer == "modules" {
|
if layer == "modules" {
|
||||||
continue // modules are with the kernel in boot v2
|
continue // modules are in the initrd with boot v2
|
||||||
}
|
}
|
||||||
|
|
||||||
layerVersion := ctx.Host.Versions[layer]
|
layerVersion := ctx.Host.Versions[layer]
|
||||||
@ -108,7 +179,7 @@ func buildBootstrap(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
return fmt.Errorf("layer %q not mapped to a version", layer)
|
return fmt.Errorf("layer %q not mapped to a version", layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
outPath, err := ctx.distFetch("layers", layer, layerVersion)
|
outPath, err := distFetch("layers", layer, layerVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -125,14 +196,24 @@ func buildBootstrap(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h := hash.New()
|
||||||
|
reader := io.TeeReader(f, h)
|
||||||
|
|
||||||
if err = arch.WriteHeader(&tar.Header{
|
if err = arch.WriteHeader(&tar.Header{
|
||||||
Name: layer + ".fs",
|
Name: layer + ".fs",
|
||||||
Size: stat.Size(),
|
Size: stat.Size(),
|
||||||
|
Mode: 0o600,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.Copy(arch, f)
|
_, err = io.Copy(arch, reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
digest := h.Sum(nil)
|
||||||
|
err = sign(layer+".fs.sig", digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
cacheCleanDelay = flag.Duration("cache-clean-delay", 10*time.Minute, "Time between cache cleanups")
|
|
||||||
)
|
|
||||||
|
|
||||||
func casCleaner() {
|
|
||||||
for range time.Tick(*cacheCleanDelay) {
|
|
||||||
if !wPublicState.Get().Store.Open {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err := cleanCAS()
|
|
||||||
if err != nil {
|
|
||||||
log.Print("warn: couldn't clean cache: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanCAS() error {
|
|
||||||
cfg, err := readConfig()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
activeTags := make([]string, len(cfg.Hosts))
|
|
||||||
|
|
||||||
for i, host := range cfg.Hosts {
|
|
||||||
// FIXME ugly hack, same as in dir2config
|
|
||||||
cfg, err := readConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, err := newRenderContext(host, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tag, err := ctx.Tag()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activeTags[i] = tag
|
|
||||||
}
|
|
||||||
|
|
||||||
tags, err := casStore.Tags()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(activeTags)
|
|
||||||
|
|
||||||
for _, tag := range tags {
|
|
||||||
idx := sort.SearchStrings(activeTags, tag)
|
|
||||||
|
|
||||||
if idx < len(activeTags) && activeTags[idx] == tag {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// tag is not present in active tags
|
|
||||||
log.Print("cache cleaner: removing tag ", tag)
|
|
||||||
if err := casStore.Remove(tag); err != nil {
|
|
||||||
log.Printf("cache cleaner: failed to remove tag %s: %v", tag, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,8 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -12,6 +15,7 @@ import (
|
|||||||
|
|
||||||
cfsslconfig "github.com/cloudflare/cfssl/config"
|
cfsslconfig "github.com/cloudflare/cfssl/config"
|
||||||
"github.com/cloudflare/cfssl/csr"
|
"github.com/cloudflare/cfssl/csr"
|
||||||
|
"github.com/cloudflare/cfssl/helpers"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"novit.tech/direktil/pkg/bootstrapconfig"
|
"novit.tech/direktil/pkg/bootstrapconfig"
|
||||||
@ -19,6 +23,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func templateFuncs(sslCfg *cfsslconfig.Config) map[string]any {
|
func templateFuncs(sslCfg *cfsslconfig.Config) map[string]any {
|
||||||
|
getKey := func(cluster, caName string) (key crypto.Signer, err error) {
|
||||||
|
ca, err := getUsableClusterCA(cluster, caName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key, err = helpers.ParsePrivateKeyPEM(ca.Key)
|
||||||
|
return
|
||||||
|
}
|
||||||
getKeyCert := func(cluster, caName, name, profile, label, reqJson string) (kc KeyCert, err error) {
|
getKeyCert := func(cluster, caName, name, profile, label, reqJson string) (kc KeyCert, err error) {
|
||||||
certReq := &csr.CertificateRequest{
|
certReq := &csr.CertificateRequest{
|
||||||
KeyRequest: csr.NewKeyRequest(),
|
KeyRequest: csr.NewKeyRequest(),
|
||||||
@ -133,6 +145,22 @@ func templateFuncs(sslCfg *cfsslconfig.Config) map[string]any {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"tls_pubkey": func(cluster, caName string) (s string, err error) {
|
||||||
|
priv, err := getKey(cluster, caName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ba, err := x509.MarshalPKIXPublicKey(priv.Public())
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("marshal public key failed: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s = base64.StdEncoding.EncodeToString(ba)
|
||||||
|
return
|
||||||
|
},
|
||||||
|
|
||||||
"tls_crt": func(cluster, caName, name, profile, label, reqJson string) (s string, err error) {
|
"tls_crt": func(cluster, caName, name, profile, label, reqJson string) (s string, err error) {
|
||||||
kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson)
|
kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
15
cmd/dkl-local-server/html.go
Normal file
15
cmd/dkl-local-server/html.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func htmlHeader(title string) string {
|
||||||
|
return `<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>` + title + `</title>
|
||||||
|
<style>@import url('/ui/style.css');@import url('/ui/app.css');</style>
|
||||||
|
</head>
|
||||||
|
<body><h1>` + title + `</h1>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
var htmlFooter = `</body>
|
||||||
|
</html>`
|
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func renderKernel(w http.ResponseWriter, r *http.Request, ctx *renderContext) error {
|
func renderKernel(w http.ResponseWriter, r *http.Request, ctx *renderContext) error {
|
||||||
path, err := ctx.distFetch("kernels", ctx.Host.Kernel)
|
path, err := distFetch("kernels", ctx.Host.Kernel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ func renderKernel(w http.ResponseWriter, r *http.Request, ctx *renderContext) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchKernel(out io.Writer, ctx *renderContext) (err error) {
|
func fetchKernel(out io.Writer, ctx *renderContext) (err error) {
|
||||||
path, err := ctx.distFetch("kernels", ctx.Host.Kernel)
|
path, err := distFetch("kernels", ctx.Host.Kernel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,11 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
restful "github.com/emicklei/go-restful"
|
restful "github.com/emicklei/go-restful"
|
||||||
swaggerui "github.com/mcluseau/go-swagger-ui"
|
swaggerui "github.com/mcluseau/go-swagger-ui"
|
||||||
"m.cluseau.fr/go/watchable/streamsse"
|
"m.cluseau.fr/go/watchable/streamsse"
|
||||||
|
|
||||||
"novit.tech/direktil/pkg/cas"
|
|
||||||
|
|
||||||
dlshtml "novit.tech/direktil/local-server/html"
|
dlshtml "novit.tech/direktil/local-server/html"
|
||||||
"novit.tech/direktil/local-server/pkg/apiutils"
|
"novit.tech/direktil/local-server/pkg/apiutils"
|
||||||
)
|
)
|
||||||
@ -30,8 +27,6 @@ var (
|
|||||||
keyFile = flag.String("tls-key", etcDir+"/server.key", "Server TLS key")
|
keyFile = flag.String("tls-key", etcDir+"/server.key", "Server TLS key")
|
||||||
|
|
||||||
autoUnlock = flag.String("auto-unlock", "", "Auto-unlock store (testing only!) env: DLS_AUTO_UNLOCK")
|
autoUnlock = flag.String("auto-unlock", "", "Auto-unlock store (testing only!) env: DLS_AUTO_UNLOCK")
|
||||||
|
|
||||||
casStore cas.Store
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -68,9 +63,6 @@ func main() {
|
|||||||
os.Setenv("DLS_AUTO_UNLOCK", "")
|
os.Setenv("DLS_AUTO_UNLOCK", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
casStore = cas.NewDir(filepath.Join(*dataDir, "cache"))
|
|
||||||
go casCleaner()
|
|
||||||
|
|
||||||
apiutils.Setup(func() {
|
apiutils.Setup(func() {
|
||||||
registerWS(restful.DefaultContainer)
|
registerWS(restful.DefaultContainer)
|
||||||
})
|
})
|
||||||
@ -80,6 +72,7 @@ func main() {
|
|||||||
staticHandler := http.FileServer(http.FS(dlshtml.FS))
|
staticHandler := http.FileServer(http.FS(dlshtml.FS))
|
||||||
http.Handle("/favicon.ico", staticHandler)
|
http.Handle("/favicon.ico", staticHandler)
|
||||||
http.Handle("/ui/", staticHandler)
|
http.Handle("/ui/", staticHandler)
|
||||||
|
http.Handle("/dist/", http.StripPrefix("/dist/", upstreamServer{}))
|
||||||
|
|
||||||
http.Handle("/public-state", streamsse.StreamHandler(wPublicState))
|
http.Handle("/public-state", streamsse.StreamHandler(wPublicState))
|
||||||
http.Handle("/state", requireAdmin(streamsse.StreamHandler(wState)))
|
http.Handle("/state", requireAdmin(streamsse.StreamHandler(wState)))
|
||||||
|
23
cmd/dkl-local-server/parsers_test.go
Normal file
23
cmd/dkl-local-server/parsers_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_parseCertDuration() {
|
||||||
|
now := time.Date(2020, time.April, 28, 12, 30, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
fmt.Println(parseCertDuration("", now))
|
||||||
|
fmt.Println(parseCertDuration("hi!", now))
|
||||||
|
fmt.Println(parseCertDuration("-2d3h", now))
|
||||||
|
fmt.Println(parseCertDuration("2d3h", now))
|
||||||
|
fmt.Println(parseCertDuration("+1y-1s", now))
|
||||||
|
|
||||||
|
// output:
|
||||||
|
// 0001-01-01 00:00:00 +0000 UTC <nil>
|
||||||
|
// 0001-01-01 00:00:00 +0000 UTC invalid duration: "hi!"
|
||||||
|
// 2020-04-26 09:30:00 +0000 UTC <nil>
|
||||||
|
// 2020-04-30 15:30:00 +0000 UTC <nil>
|
||||||
|
// 2021-04-28 12:29:59 +0000 UTC <nil>
|
||||||
|
}
|
@ -10,9 +10,11 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
cfsslconfig "github.com/cloudflare/cfssl/config"
|
cfsslconfig "github.com/cloudflare/cfssl/config"
|
||||||
restful "github.com/emicklei/go-restful"
|
restful "github.com/emicklei/go-restful"
|
||||||
@ -35,12 +37,7 @@ type renderContext struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func renderCtx(w http.ResponseWriter, r *http.Request, ctx *renderContext, what string,
|
func renderCtx(w http.ResponseWriter, r *http.Request, ctx *renderContext, what string,
|
||||||
create func(out io.Writer, ctx *renderContext) error) error {
|
create func(out io.Writer, ctx *renderContext) error) (err error) {
|
||||||
|
|
||||||
tag, err := ctx.Tag()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.CmdLine = r.URL.Query().Get(cmdlineParam.Data().Name)
|
ctx.CmdLine = r.URL.Query().Get(cmdlineParam.Data().Name)
|
||||||
|
|
||||||
@ -49,19 +46,26 @@ func renderCtx(w http.ResponseWriter, r *http.Request, ctx *renderContext, what
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get it or create it
|
// get it or create it
|
||||||
content, meta, err := casStore.GetOrCreate(tag, what, func(out io.Writer) error {
|
outfile, err := os.CreateTemp("/tmp", "dls."+what+".")
|
||||||
log.Printf("building %s for %q", what, ctx.Host.Name)
|
|
||||||
return create(out, ctx)
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.Remove(outfile.Name())
|
||||||
|
|
||||||
|
log.Printf("building %s for %q", what, ctx.Host.Name)
|
||||||
|
err = create(outfile, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// serve it
|
// serve it
|
||||||
log.Printf("sending %s for %q", what, ctx.Host.Name)
|
log.Printf("sending %s for %q", what, ctx.Host.Name)
|
||||||
http.ServeContent(w, r, what, meta.ModTime(), content)
|
|
||||||
return nil
|
outfile.Seek(0, io.SeekStart)
|
||||||
|
io.Copy(w, outfile)
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func sslConfigFromLocalConfig(cfg *localconfig.Config) (sslCfg *cfsslconfig.Config, err error) {
|
func sslConfigFromLocalConfig(cfg *localconfig.Config) (sslCfg *cfsslconfig.Config, err error) {
|
||||||
@ -110,6 +114,7 @@ func (ctx *renderContext) BootstrapConfig() (ba []byte, cfg *bsconfig.Config, er
|
|||||||
|
|
||||||
cfg = &bsconfig.Config{}
|
cfg = &bsconfig.Config{}
|
||||||
if err = yaml.Unmarshal(ba, cfg); err != nil {
|
if err = yaml.Unmarshal(ba, cfg); err != nil {
|
||||||
|
log.Print("invalid bootstrap config yaml:\n", string(ba))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +139,7 @@ func (ctx *renderContext) render(templateText string) (ba []byte, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *renderContext) distFilePath(path ...string) string {
|
func distFilePath(path ...string) string {
|
||||||
return filepath.Join(append([]string{*dataDir, "dist"}, path...)...)
|
return filepath.Join(append([]string{*dataDir, "dist"}, path...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +153,7 @@ func (ctx *renderContext) Tag() (string, error) {
|
|||||||
|
|
||||||
enc := yaml.NewEncoder(h)
|
enc := yaml.NewEncoder(h)
|
||||||
|
|
||||||
for _, o := range []interface{}{cfg, ctx} {
|
for _, o := range []any{cfg, ctx} {
|
||||||
if err := enc.Encode(o); err != nil {
|
if err := enc.Encode(o); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -157,21 +162,6 @@ func (ctx *renderContext) Tag() (string, error) {
|
|||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func asMap(v interface{}) map[string]interface{} {
|
|
||||||
ba, err := yaml.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // shouldn't happen
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make(map[string]interface{})
|
|
||||||
|
|
||||||
if err := yaml.Unmarshal(ba, result); err != nil {
|
|
||||||
panic(err) // shouldn't happen
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *renderContext) TemplateFuncs() map[string]any {
|
func (ctx *renderContext) TemplateFuncs() map[string]any {
|
||||||
funcs := templateFuncs(ctx.SSLConfig)
|
funcs := templateFuncs(ctx.SSLConfig)
|
||||||
|
|
||||||
@ -187,6 +177,14 @@ func (ctx *renderContext) TemplateFuncs() map[string]any {
|
|||||||
return hex.EncodeToString(ba[:])
|
return hex.EncodeToString(ba[:])
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ssh_user_ca": func(path, cluster string) (s string, err error) {
|
||||||
|
userCA, err := sshCAPubKey(cluster)
|
||||||
|
return asYaml([]config.FileDef{{
|
||||||
|
Path: path,
|
||||||
|
Mode: 0644,
|
||||||
|
Content: string(userCA),
|
||||||
|
}})
|
||||||
|
},
|
||||||
"ssh_host_keys": func(dir, cluster, host string) (s string, err error) {
|
"ssh_host_keys": func(dir, cluster, host string) (s string, err error) {
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = ctx.Host.Name
|
host = ctx.Host.Name
|
||||||
@ -240,6 +238,32 @@ func (ctx *renderContext) TemplateFuncs() map[string]any {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
},
|
||||||
|
"asset_download_token": func(asset string, params ...string) (token string, err error) {
|
||||||
|
now := time.Now()
|
||||||
|
exp := now.Add(24 * time.Hour) // expire in 24h by default
|
||||||
|
if len(params) != 0 {
|
||||||
|
exp, err = parseCertDuration(params[0], now)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set := DownloadSet{
|
||||||
|
Expiry: exp,
|
||||||
|
Items: []DownloadSetItem{
|
||||||
|
{
|
||||||
|
Kind: "host",
|
||||||
|
Name: ctx.Host.Name,
|
||||||
|
Assets: []string{asset},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
privKey, _ := dlsSigningKeys()
|
||||||
|
token = set.Signed(privKey)
|
||||||
|
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
@ -9,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/cloudflare/cfssl/certinfo"
|
"github.com/cloudflare/cfssl/certinfo"
|
||||||
"github.com/cloudflare/cfssl/config"
|
"github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/cloudflare/cfssl/helpers/derhelpers"
|
||||||
"github.com/cloudflare/cfssl/log"
|
"github.com/cloudflare/cfssl/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,3 +75,33 @@ func checkCertUsable(certPEM []byte) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dlsSigningKeys() (ed25519.PrivateKey, ed25519.PublicKey) {
|
||||||
|
var signerDER []byte
|
||||||
|
|
||||||
|
if err := readSecret("signer", &signerDER); os.IsNotExist(err) {
|
||||||
|
_, key, err := ed25519.GenerateKey(nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signerDER, err = derhelpers.MarshalEd25519PrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSecret("signer", signerDER)
|
||||||
|
} else if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkeyGeneric, err := derhelpers.ParseEd25519PrivateKey(signerDER)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkey := pkeyGeneric.(ed25519.PrivateKey)
|
||||||
|
pubkey := pkey.Public().(ed25519.PublicKey)
|
||||||
|
|
||||||
|
return pkey, pubkey
|
||||||
|
}
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/dsa"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"encoding/pem"
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/asn1"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sshHostKeys = KVSecrets[[]SSHKeyPair]{"hosts/ssh-host-keys"}
|
var sshHostKeys = KVSecrets[[]SSHKeyPair]{"hosts/ssh-host-keys"}
|
||||||
@ -104,64 +106,106 @@ genLoop:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshKeyGenDSA() (data []byte, pubKey interface{}, err error) {
|
var sshCAKeys = KVSecrets[string]{"ssh-ca-keys"}
|
||||||
privKey := &dsa.PrivateKey{}
|
|
||||||
|
|
||||||
err = dsa.GenerateParameters(&privKey.Parameters, rand.Reader, dsa.L1024N160)
|
func sshCAKey(cluster string) (caKeyPem string, err error) {
|
||||||
|
storeKey := "clusters/" + cluster
|
||||||
|
caKeyPem, _, err = sshCAKeys.Get(storeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dsa.GenerateKey(privKey, rand.Reader)
|
if caKeyPem == "" {
|
||||||
|
_, pk, err := ed25519.GenerateKey(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err = asn1.Marshal(*privKey)
|
pemBlock, err := ssh.MarshalPrivateKey(crypto.PrivateKey(pk), "")
|
||||||
//data, err = x509.MarshalPKCS8PrivateKey(privKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
pubKey = privKey.PublicKey
|
caKeyPem = string(pem.EncodeToMemory(pemBlock))
|
||||||
return
|
sshCAKeys.Put(storeKey, caKeyPem)
|
||||||
}
|
|
||||||
|
|
||||||
func sshKeyGenRSA() (data []byte, pubKey interface{}, err error) {
|
|
||||||
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data = x509.MarshalPKCS1PrivateKey(privKey)
|
|
||||||
pubKey = privKey.Public()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func sshKeyGenECDSA() (data []byte, pubKey interface{}, err error) {
|
|
||||||
privKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err = x509.MarshalPKCS8PrivateKey(privKey)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKey = privKey.Public()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func sshKeyGenED25519() (data []byte, pubKey interface{}, err error) {
|
|
||||||
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
|
|
||||||
|
|
||||||
data, err = x509.MarshalPKCS8PrivateKey(privKey)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sshCAPubKey(cluster string) (pubKey []byte, err error) {
|
||||||
|
keyPem, err := sshCAKey(cluster)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
k, err := ssh.ParsePrivateKey([]byte(keyPem))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey = ssh.MarshalAuthorizedKey(k.PublicKey())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// principal: user (login) to allow (ie: "root")
|
||||||
|
// validity: ssh-keygen validity string (ie: "+1h", "202506280811:202506281011", ""=forever)
|
||||||
|
// options: ssh-keygen options (ie: "force-command=/bin/date +\"%F %T\"", "source-address=192.168.1.0/24,192.168.42.0/24"
|
||||||
|
func sshCASign(cluster string, userPubKey []byte, principal, validity string, options ...string) (cert []byte, err error) {
|
||||||
|
caKey, err := sshCAKey(cluster)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, identity, _, _, err := ssh.ParseAuthorizedKey(userPubKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userPubKeyFile, err := os.CreateTemp("/tmp", "user.pub")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.Remove(userPubKeyFile.Name())
|
||||||
|
|
||||||
|
_, err = io.Copy(userPubKeyFile, bytes.NewBuffer(userPubKey))
|
||||||
|
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)
|
||||||
|
|
||||||
|
if validity != "" {
|
||||||
|
cmd.Args = append(cmd.Args, "-V", validity)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range options {
|
||||||
|
cmd.Args = append(cmd.Args, "-O", opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Args = append(cmd.Args, userPubKeyFile.Name())
|
||||||
|
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
cmd.Stdin = bytes.NewBuffer([]byte(caKey))
|
||||||
|
cmd.Stderr = stderr
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("ssh-keygen sign failed: %s", strings.TrimSpace(stderr.String()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
certFile := userPubKeyFile.Name() + "-cert.pub"
|
||||||
|
cert, err = os.ReadFile(certFile)
|
||||||
|
|
||||||
|
os.Remove(certFile)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -72,13 +72,20 @@ func (_ CA) newReq() *csr.CertificateRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ca CA) ParseKey() (key crypto.Signer, err error) {
|
||||||
|
return helpers.ParsePrivateKeyPEM(ca.Key)
|
||||||
|
}
|
||||||
|
func (ca CA) ParseCert() (cert *x509.Certificate, err error) {
|
||||||
|
return helpers.ParseCertificatePEM(ca.Cert)
|
||||||
|
}
|
||||||
|
|
||||||
func (ca CA) Signer(policy *config.Signing) (result *local.Signer, err error) {
|
func (ca CA) Signer(policy *config.Signing) (result *local.Signer, err error) {
|
||||||
caCert, err := helpers.ParseCertificatePEM(ca.Cert)
|
caCert, err := ca.ParseCert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
caKey, err := helpers.ParsePrivateKeyPEM(ca.Key)
|
caKey, err := ca.ParseKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -148,7 +155,7 @@ func getUsableKeyCert(cluster, caName, name, profile, label string, req *csr.Cer
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = cert.Verify(x509.VerifyOptions{Roots: pool})
|
_, err = cert.Verify(x509.VerifyOptions{Roots: pool, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}})
|
||||||
return
|
return
|
||||||
}()
|
}()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
63
cmd/dkl-local-server/udev.go
Normal file
63
cmd/dkl-local-server/udev.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Simulate a udev run for our needs
|
||||||
|
func syncSysToDev() {
|
||||||
|
// loop devices
|
||||||
|
sysPaths, _ := filepath.Glob("/sys/devices/virtual/block/loop*/**/dev")
|
||||||
|
for _, sysPath := range sysPaths {
|
||||||
|
mknodBlk(sysPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mknodBlk(sysPath string) {
|
||||||
|
devPath := "/dev/" + filepath.Base(filepath.Dir(sysPath))
|
||||||
|
if _, err := os.Stat(devPath); os.IsNotExist(err) {
|
||||||
|
// ok
|
||||||
|
} else if err != nil {
|
||||||
|
log.Printf("stat %s failed: %v", devPath, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
return // exists
|
||||||
|
}
|
||||||
|
|
||||||
|
devBytes, err := os.ReadFile(sysPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("read %s failed: %v", sysPath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
devBytes = bytes.TrimSpace(devBytes)
|
||||||
|
|
||||||
|
// rust: let Some(dev) = devBytes.split_once(':').filter_map(|a,b| Some(mkdev(a.parse().ok()?, b.parse().ok()?)));
|
||||||
|
majorStr, minorStr, ok := strings.Cut(string(devBytes), ":")
|
||||||
|
if !ok {
|
||||||
|
log.Printf("%s: invalid dev string: %s", sysPath, string(devBytes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
major, err := strconv.ParseUint(majorStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%s: invalid major: %q", sysPath, majorStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
minor, err := strconv.ParseUint(minorStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%s: invalid minor: %q", sysPath, minorStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
devMajMin := int(unix.Mkdev(uint32(major), uint32(minor)))
|
||||||
|
|
||||||
|
log.Printf("mknod %s b %d %d", devPath, major, minor)
|
||||||
|
unix.Mknod(devPath, syscall.S_IFBLK|0o0600, devMajMin)
|
||||||
|
}
|
@ -9,9 +9,11 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
gopath "path"
|
gopath "path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
@ -22,8 +24,22 @@ var (
|
|||||||
upstreamURL = flag.String("upstream", "https://dkl.novit.io/dist", "Upstream server for dist elements")
|
upstreamURL = flag.String("upstream", "https://dkl.novit.io/dist", "Upstream server for dist elements")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ctx *renderContext) distFetch(path ...string) (outPath string, err error) {
|
type upstreamServer struct{}
|
||||||
outPath = ctx.distFilePath(path...)
|
|
||||||
|
func (_ upstreamServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
path := path.Clean(req.URL.Path)
|
||||||
|
outPath, err := distFetch(strings.Split(path, "/")...)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadGateway)
|
||||||
|
w.Write([]byte(err.Error() + "\n"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ServeFile(w, req, outPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func distFetch(path ...string) (outPath string, err error) {
|
||||||
|
outPath = distFilePath(path...)
|
||||||
|
|
||||||
if _, err = os.Stat(outPath); err == nil {
|
if _, err = os.Stat(outPath); err == nil {
|
||||||
return
|
return
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/cloudflare/cfssl/csr"
|
||||||
|
"github.com/cloudflare/cfssl/signer"
|
||||||
restful "github.com/emicklei/go-restful"
|
restful "github.com/emicklei/go-restful"
|
||||||
|
|
||||||
"novit.tech/direktil/local-server/pkg/mime"
|
"novit.tech/direktil/local-server/pkg/mime"
|
||||||
@ -121,3 +129,203 @@ func wsClusterSignedCert(req *restful.Request, resp *restful.Response) {
|
|||||||
resp.AddHeader("Content-Disposition", "attachment; filename="+strconv.Quote(clusterName+"_"+caName+"_"+url.PathEscape(name)+".crt"))
|
resp.AddHeader("Content-Disposition", "attachment; filename="+strconv.Quote(clusterName+"_"+caName+"_"+url.PathEscape(name)+".crt"))
|
||||||
resp.Write(kc.Cert)
|
resp.Write(kc.Cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SSHSignReq struct {
|
||||||
|
PubKey string
|
||||||
|
Principal string
|
||||||
|
Validity string
|
||||||
|
Options []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsClusterSSHUserCAPubKey(req *restful.Request, resp *restful.Response) {
|
||||||
|
clusterName := req.PathParameter("cluster-name")
|
||||||
|
|
||||||
|
pubkey, err := sshCAPubKey(clusterName)
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Write(pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsClusterSSHUserCASign(req *restful.Request, resp *restful.Response) {
|
||||||
|
clusterName := req.PathParameter("cluster-name")
|
||||||
|
|
||||||
|
signReq := SSHSignReq{}
|
||||||
|
err := req.ReadEntity(&signReq)
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Truncate(time.Second)
|
||||||
|
notBefore, notAfter, err := parseCertDurationRange(signReq.Validity, now)
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, fmt.Errorf("invalid validity: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sshTimestamp = "20060102150405Z"
|
||||||
|
|
||||||
|
validity := notBefore.Format(sshTimestamp) + ":"
|
||||||
|
if notAfter.IsZero() {
|
||||||
|
validity += "forever"
|
||||||
|
} else {
|
||||||
|
validity += notAfter.Format(sshTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("sign ssh public key, validity %s -> %s", signReq.Validity, validity)
|
||||||
|
|
||||||
|
cert, err := sshCASign(clusterName, []byte(signReq.PubKey), signReq.Principal, validity, signReq.Options...)
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Write(cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
type KubeSignReq struct {
|
||||||
|
CSR string
|
||||||
|
User string
|
||||||
|
Group string
|
||||||
|
Validity string
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsClusterKubeCASign(req *restful.Request, resp *restful.Response) {
|
||||||
|
clusterName := req.PathParameter("cluster-name")
|
||||||
|
|
||||||
|
signReq := KubeSignReq{}
|
||||||
|
err := req.ReadEntity(&signReq)
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Truncate(time.Second)
|
||||||
|
notBefore, notAfter, err := parseCertDurationRange(signReq.Validity, now)
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, fmt.Errorf("invalid validity: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var names []csr.Name
|
||||||
|
if signReq.Group != "" {
|
||||||
|
names = []csr.Name{{O: signReq.Group}}
|
||||||
|
}
|
||||||
|
|
||||||
|
ca, err := getUsableClusterCA(clusterName, "cluster")
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, fmt.Errorf("get cluster CA failed: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caSigner, err := ca.Signer(&config.Signing{
|
||||||
|
Default: &config.SigningProfile{
|
||||||
|
Usage: []string{"client auth"},
|
||||||
|
Expiry: notAfter.Sub(now),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
csr := signer.SignRequest{
|
||||||
|
Request: signReq.CSR,
|
||||||
|
Subject: &signer.Subject{
|
||||||
|
CN: signReq.User,
|
||||||
|
Names: names,
|
||||||
|
},
|
||||||
|
NotBefore: notBefore,
|
||||||
|
NotAfter: notAfter,
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := caSigner.Sign(csr)
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Write(cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCertDurationRange(d string, now time.Time) (notBefore, notAfter time.Time, err error) {
|
||||||
|
if d == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d1, d2, ok := strings.Cut(d, ":")
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
notBefore, err = parseCertDuration(d1, now)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
notAfter, err = parseCertDuration(d2, now)
|
||||||
|
} else {
|
||||||
|
notAfter, err = parseCertDuration(d, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if notBefore.IsZero() {
|
||||||
|
notBefore = now.Add(-5 * time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var durRegex = regexp.MustCompile("^([+-]?)([0-9]+)([yMdwhms])")
|
||||||
|
|
||||||
|
func parseCertDuration(d string, now time.Time) (t time.Time, err error) {
|
||||||
|
if d == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
direction := 1
|
||||||
|
t = now
|
||||||
|
|
||||||
|
for d != "" {
|
||||||
|
match := durRegex.FindStringSubmatch(d)
|
||||||
|
if match == nil {
|
||||||
|
t = time.Time{}
|
||||||
|
err = errors.New("invalid duration: " + strconv.Quote(d))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d = d[len(match[0]):]
|
||||||
|
|
||||||
|
switch match[1] {
|
||||||
|
case "+":
|
||||||
|
direction = 1
|
||||||
|
case "-":
|
||||||
|
direction = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
qty, _ := strconv.Atoi(match[2])
|
||||||
|
unit := match[3]
|
||||||
|
|
||||||
|
switch unit {
|
||||||
|
case "y":
|
||||||
|
t = t.AddDate(qty*direction, 0, 0)
|
||||||
|
case "M":
|
||||||
|
t = t.AddDate(0, qty*direction, 0)
|
||||||
|
case "d":
|
||||||
|
t = t.AddDate(0, 0, qty*direction)
|
||||||
|
case "w":
|
||||||
|
t = t.AddDate(0, 0, 7*qty*direction)
|
||||||
|
case "h":
|
||||||
|
t = t.Add(time.Duration(qty*direction) * time.Hour)
|
||||||
|
case "m":
|
||||||
|
t = t.Add(time.Duration(qty*direction) * time.Minute)
|
||||||
|
case "s":
|
||||||
|
t = t.Add(time.Duration(qty*direction) * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
292
cmd/dkl-local-server/ws-download-set.go
Normal file
292
cmd/dkl-local-server/ws-download-set.go
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/base32"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
restful "github.com/emicklei/go-restful"
|
||||||
|
"github.com/pierrec/lz4"
|
||||||
|
"m.cluseau.fr/go/httperr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func globMatch(pattern, value string) bool {
|
||||||
|
ok, _ := filepath.Match(pattern, value)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownloadSet struct {
|
||||||
|
Expiry time.Time
|
||||||
|
Items []DownloadSetItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DownloadSet) Contains(kind, name, asset string) bool {
|
||||||
|
for _, item := range s.Items {
|
||||||
|
if item.Kind == kind && globMatch(item.Name, name) &&
|
||||||
|
slices.Contains(item.Assets, asset) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DownloadSet) Encode() string {
|
||||||
|
buf := new(strings.Builder)
|
||||||
|
s.EncodeTo(buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DownloadSet) EncodeTo(buf *strings.Builder) {
|
||||||
|
buf.WriteString(strconv.FormatInt(s.Expiry.Unix(), 16))
|
||||||
|
|
||||||
|
for _, item := range s.Items {
|
||||||
|
buf.WriteByte('|')
|
||||||
|
item.EncodeTo(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DownloadSet) Decode(encoded string) (err error) {
|
||||||
|
exp, rem, _ := strings.Cut(encoded, "|")
|
||||||
|
|
||||||
|
expUnix, err := strconv.ParseInt(exp, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Expiry = time.Unix(expUnix, 0)
|
||||||
|
|
||||||
|
if rem == "" {
|
||||||
|
s.Items = nil
|
||||||
|
} else {
|
||||||
|
itemStrs := strings.Split(rem, "|")
|
||||||
|
s.Items = make([]DownloadSetItem, len(itemStrs))
|
||||||
|
for i, itemStr := range itemStrs {
|
||||||
|
s.Items[i].Decode(itemStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DownloadSet) Signed(privKey ed25519.PrivateKey) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
{
|
||||||
|
setBytes := []byte(s.Encode())
|
||||||
|
|
||||||
|
w := lz4.NewWriter(buf)
|
||||||
|
w.Write(setBytes)
|
||||||
|
w.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
setBytes := buf.Bytes()
|
||||||
|
sig := ed25519.Sign(privKey, setBytes)
|
||||||
|
|
||||||
|
buf = bytes.NewBuffer(make([]byte, 0, 1+len(sig)+len(setBytes)))
|
||||||
|
buf.WriteByte(byte(len(sig)))
|
||||||
|
buf.Write(sig)
|
||||||
|
buf.Write(setBytes)
|
||||||
|
|
||||||
|
enc := base32.StdEncoding.WithPadding(base32.NoPadding)
|
||||||
|
return enc.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownloadSetItem struct {
|
||||||
|
Kind string
|
||||||
|
Name string
|
||||||
|
Assets []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i DownloadSetItem) EncodeTo(buf *strings.Builder) {
|
||||||
|
kind := i.Kind
|
||||||
|
switch kind {
|
||||||
|
case "host":
|
||||||
|
kind = "h"
|
||||||
|
case "cluster":
|
||||||
|
kind = "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(kind)
|
||||||
|
buf.WriteByte(':')
|
||||||
|
buf.WriteString(i.Name)
|
||||||
|
|
||||||
|
for _, asset := range i.Assets {
|
||||||
|
buf.WriteByte(':')
|
||||||
|
buf.WriteString(asset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *DownloadSetItem) Decode(encoded string) {
|
||||||
|
rem := encoded
|
||||||
|
i.Kind, rem, _ = strings.Cut(rem, ":")
|
||||||
|
|
||||||
|
switch i.Kind {
|
||||||
|
case "h":
|
||||||
|
i.Kind = "host"
|
||||||
|
case "c":
|
||||||
|
i.Kind = "cluster"
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Name, rem, _ = strings.Cut(rem, ":")
|
||||||
|
|
||||||
|
if rem == "" {
|
||||||
|
i.Assets = nil
|
||||||
|
} else {
|
||||||
|
i.Assets = strings.Split(rem, ":")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownloadSetReq struct {
|
||||||
|
Expiry string
|
||||||
|
Items []DownloadSetItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsSignDownloadSet(req *restful.Request, resp *restful.Response) {
|
||||||
|
setReq := DownloadSetReq{}
|
||||||
|
if err := req.ReadEntity(&setReq); err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exp, err := parseCertDuration(setReq.Expiry, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
set := DownloadSet{
|
||||||
|
Expiry: exp,
|
||||||
|
Items: setReq.Items,
|
||||||
|
}
|
||||||
|
|
||||||
|
privKey, _ := dlsSigningKeys()
|
||||||
|
resp.WriteEntity(set.Signed(privKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDlSet(req *restful.Request) (*DownloadSet, *httperr.Error) {
|
||||||
|
setStr := req.QueryParameter("set")
|
||||||
|
|
||||||
|
setBytes, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(setStr)
|
||||||
|
if err != nil {
|
||||||
|
err := httperr.BadRequest("invalid set")
|
||||||
|
return nil, &err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(setBytes) == 0 {
|
||||||
|
err := httperr.BadRequest("invalid set")
|
||||||
|
return nil, &err
|
||||||
|
}
|
||||||
|
|
||||||
|
sigLen := int(setBytes[0])
|
||||||
|
setBytes = setBytes[1:]
|
||||||
|
|
||||||
|
if len(setBytes) < sigLen {
|
||||||
|
err := httperr.BadRequest("invalid set")
|
||||||
|
return nil, &err
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := setBytes[:sigLen]
|
||||||
|
setBytes = setBytes[sigLen:]
|
||||||
|
|
||||||
|
_, pubkey := dlsSigningKeys()
|
||||||
|
if !ed25519.Verify(pubkey, setBytes, sig) {
|
||||||
|
err := httperr.BadRequest("invalid signature")
|
||||||
|
return nil, &err
|
||||||
|
}
|
||||||
|
|
||||||
|
setBytes, err = io.ReadAll(lz4.NewReader(bytes.NewBuffer(setBytes)))
|
||||||
|
if err != nil {
|
||||||
|
err := httperr.BadRequest("invalid data")
|
||||||
|
return nil, &err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(setBytes))
|
||||||
|
|
||||||
|
set := DownloadSet{}
|
||||||
|
if err := set.Decode(string(setBytes)); err != nil {
|
||||||
|
err := httperr.BadRequest("invalid set: " + err.Error())
|
||||||
|
return nil, &err
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Now().After(set.Expiry) {
|
||||||
|
err := httperr.BadRequest("set expired")
|
||||||
|
return nil, &err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &set, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsDownloadSetAsset(req *restful.Request, resp *restful.Response) {
|
||||||
|
set, err := getDlSet(req)
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, *err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := req.PathParameter("kind")
|
||||||
|
name := req.PathParameter("name")
|
||||||
|
asset := req.PathParameter("asset")
|
||||||
|
|
||||||
|
if !set.Contains(kind, name, asset) {
|
||||||
|
wsNotFound(resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadAsset(req, resp, kind, name, asset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsDownloadSet(req *restful.Request, resp *restful.Response) {
|
||||||
|
setStr := req.QueryParameter("set")
|
||||||
|
set, err := getDlSet(req)
|
||||||
|
if err != nil {
|
||||||
|
resp.WriteHeader(err.Status)
|
||||||
|
resp.Write([]byte(htmlHeader(err.Error())))
|
||||||
|
resp.Write([]byte(htmlFooter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.WriteString(htmlHeader("Download set"))
|
||||||
|
|
||||||
|
cfg, err2 := readConfig()
|
||||||
|
if err2 != nil {
|
||||||
|
wsError(resp, err2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range set.Items {
|
||||||
|
names := make([]string, 0)
|
||||||
|
switch item.Kind {
|
||||||
|
case "cluster":
|
||||||
|
for _, c := range cfg.Clusters {
|
||||||
|
if globMatch(item.Name, c.Name) {
|
||||||
|
names = append(names, c.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "host":
|
||||||
|
for _, h := range cfg.Hosts {
|
||||||
|
if globMatch(item.Name, h.Name) {
|
||||||
|
names = append(names, h.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
fmt.Fprintf(buf, "<h2>%s %s</h2>", strings.Title(item.Kind), name)
|
||||||
|
fmt.Fprintf(buf, "<p class=\"download-links\">\n")
|
||||||
|
for _, asset := range item.Assets {
|
||||||
|
fmt.Fprintf(buf, " <a href=\"/public/download-set/%s/%s/%s?set=%s\" download>%s</a>\n", item.Kind, name, asset, setStr, asset)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, `</p>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(htmlFooter)
|
||||||
|
buf.WriteTo(resp)
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
restful "github.com/emicklei/go-restful"
|
restful "github.com/emicklei/go-restful"
|
||||||
@ -53,7 +56,7 @@ func wsAuthorizeDownload(req *restful.Request, resp *restful.Response) {
|
|||||||
resp.WriteAsJson(token)
|
resp.WriteAsJson(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func wsDownload(req *restful.Request, resp *restful.Response) {
|
func wsDownloadAsset(req *restful.Request, resp *restful.Response) {
|
||||||
token := req.PathParameter("token")
|
token := req.PathParameter("token")
|
||||||
asset := req.PathParameter("asset")
|
asset := req.PathParameter("asset")
|
||||||
|
|
||||||
@ -102,6 +105,10 @@ func wsDownload(req *restful.Request, resp *restful.Response) {
|
|||||||
|
|
||||||
log.Printf("download via token: %s %q asset %q", spec.Kind, spec.Name, asset)
|
log.Printf("download via token: %s %q asset %q", spec.Kind, spec.Name, asset)
|
||||||
|
|
||||||
|
downloadAsset(req, resp, spec.Kind, spec.Name, asset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadAsset(req *restful.Request, resp *restful.Response, kind, name, asset string) {
|
||||||
cfg, err := readConfig()
|
cfg, err := readConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wsError(resp, err)
|
wsError(resp, err)
|
||||||
@ -109,12 +116,12 @@ func wsDownload(req *restful.Request, resp *restful.Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setHeader := func(ext string) {
|
setHeader := func(ext string) {
|
||||||
resp.AddHeader("Content-Disposition", "attachment; filename="+strconv.Quote(spec.Kind+"_"+spec.Name+"_"+asset+ext))
|
resp.AddHeader("Content-Disposition", "attachment; filename="+strconv.Quote(kind+"_"+name+"_"+asset+ext))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch spec.Kind {
|
switch kind {
|
||||||
case "cluster":
|
case "cluster":
|
||||||
cluster := cfg.ClusterByName(spec.Name)
|
cluster := cfg.ClusterByName(name)
|
||||||
if cluster == nil {
|
if cluster == nil {
|
||||||
wsNotFound(resp)
|
wsNotFound(resp)
|
||||||
return
|
return
|
||||||
@ -130,7 +137,7 @@ func wsDownload(req *restful.Request, resp *restful.Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "host":
|
case "host":
|
||||||
host := hostOrTemplate(cfg, spec.Name)
|
host := hostOrTemplate(cfg, name)
|
||||||
if host == nil {
|
if host == nil {
|
||||||
wsNotFound(resp)
|
wsNotFound(resp)
|
||||||
return
|
return
|
||||||
@ -149,3 +156,44 @@ func wsDownload(req *restful.Request, resp *restful.Response) {
|
|||||||
wsNotFound(resp)
|
wsNotFound(resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wsDownload(req *restful.Request, resp *restful.Response) {
|
||||||
|
if strings.HasSuffix(req.Request.URL.Path, "/") {
|
||||||
|
wsDownloadPage(req, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token := req.PathParameter("token")
|
||||||
|
|
||||||
|
spec, ok := wState.Get().Downloads[token]
|
||||||
|
if !ok {
|
||||||
|
wsNotFound(resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.WriteEntity(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsDownloadPage(req *restful.Request, resp *restful.Response) {
|
||||||
|
token := req.PathParameter("token")
|
||||||
|
|
||||||
|
spec, ok := wState.Get().Downloads[token]
|
||||||
|
if !ok {
|
||||||
|
resp.WriteHeader(http.StatusNotFound)
|
||||||
|
resp.Write([]byte(htmlHeader("Token not found")))
|
||||||
|
resp.Write([]byte(htmlFooter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.WriteString(htmlHeader(fmt.Sprintf("Token assets: %s %s", spec.Kind, spec.Name)))
|
||||||
|
|
||||||
|
buf.WriteString("<ul>")
|
||||||
|
for _, asset := range spec.Assets {
|
||||||
|
fmt.Fprintf(buf, "<li><a href=\"%s\" download>%s</a></li>\n", asset, asset)
|
||||||
|
}
|
||||||
|
buf.WriteString("</ul>")
|
||||||
|
|
||||||
|
buf.WriteString(htmlFooter)
|
||||||
|
buf.WriteTo(resp)
|
||||||
|
}
|
||||||
|
@ -30,6 +30,7 @@ func (ws wsHost) register(rws *restful.WebService, alterRB func(*restful.RouteBu
|
|||||||
|
|
||||||
for _, rb := range []*restful.RouteBuilder{
|
for _, rb := range []*restful.RouteBuilder{
|
||||||
rws.GET("").To(ws.get).
|
rws.GET("").To(ws.get).
|
||||||
|
Produces(mime.JSON).
|
||||||
Doc("Get the "+ws.hostDoc+"'s details").
|
Doc("Get the "+ws.hostDoc+"'s details").
|
||||||
Returns(200, "OK", localconfig.Host{}),
|
Returns(200, "OK", localconfig.Host{}),
|
||||||
|
|
||||||
@ -171,7 +172,6 @@ func renderHost(w http.ResponseWriter, r *http.Request, what string, host *local
|
|||||||
switch what {
|
switch what {
|
||||||
case "config":
|
case "config":
|
||||||
err = renderConfig(w, r, ctx, false)
|
err = renderConfig(w, r, ctx, false)
|
||||||
|
|
||||||
case "config.json":
|
case "config.json":
|
||||||
err = renderConfig(w, r, ctx, true)
|
err = renderConfig(w, r, ctx, true)
|
||||||
|
|
||||||
@ -180,22 +180,10 @@ func renderHost(w http.ResponseWriter, r *http.Request, what string, host *local
|
|||||||
|
|
||||||
case "kernel":
|
case "kernel":
|
||||||
err = renderKernel(w, r, ctx)
|
err = renderKernel(w, r, ctx)
|
||||||
|
|
||||||
// boot v2
|
|
||||||
case "bootstrap-config":
|
|
||||||
err = renderBootstrapConfig(w, r, ctx, false)
|
|
||||||
case "bootstrap-config.json":
|
|
||||||
err = renderBootstrapConfig(w, r, ctx, true)
|
|
||||||
case "initrd":
|
case "initrd":
|
||||||
err = renderCtx(w, r, ctx, what, buildInitrd)
|
err = renderCtx(w, r, ctx, what, buildInitrd)
|
||||||
case "bootstrap.tar":
|
case "bootstrap.tar":
|
||||||
err = renderCtx(w, r, ctx, what, buildBootstrap)
|
err = renderCtx(w, r, ctx, what, buildBootstrap)
|
||||||
case "boot.iso":
|
|
||||||
err = renderCtx(w, r, ctx, what, buildBootISO)
|
|
||||||
case "boot.tar":
|
|
||||||
err = renderCtx(w, r, ctx, what, buildBootTar)
|
|
||||||
case "boot-efi.tar":
|
|
||||||
err = renderCtx(w, r, ctx, what, buildBootEFITar)
|
|
||||||
|
|
||||||
case "boot.img":
|
case "boot.img":
|
||||||
err = renderCtx(w, r, ctx, what, buildBootImg)
|
err = renderCtx(w, r, ctx, what, buildBootImg)
|
||||||
@ -213,6 +201,19 @@ func renderHost(w http.ResponseWriter, r *http.Request, what string, host *local
|
|||||||
err = renderCtx(w, r, ctx, what, qemuImgBootImg("vmdk"))
|
err = renderCtx(w, r, ctx, what, qemuImgBootImg("vmdk"))
|
||||||
case "boot.vpc":
|
case "boot.vpc":
|
||||||
err = renderCtx(w, r, ctx, what, qemuImgBootImg("vpc"))
|
err = renderCtx(w, r, ctx, what, qemuImgBootImg("vpc"))
|
||||||
|
case "boot.iso":
|
||||||
|
err = renderCtx(w, r, ctx, what, buildBootISO)
|
||||||
|
|
||||||
|
case "boot.tar":
|
||||||
|
err = renderCtx(w, r, ctx, what, buildBootTar)
|
||||||
|
case "boot-efi.tar":
|
||||||
|
err = renderCtx(w, r, ctx, what, buildBootEFITar)
|
||||||
|
|
||||||
|
// boot v2
|
||||||
|
case "bootstrap-config":
|
||||||
|
err = renderBootstrapConfig(w, r, ctx, false)
|
||||||
|
case "bootstrap-config.json":
|
||||||
|
err = renderBootstrapConfig(w, r, ctx, true)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
|
@ -28,7 +28,6 @@ func (hft HostFromTemplate) ClusterName(cfg *localconfig.Config) string {
|
|||||||
func hostOrTemplate(cfg *localconfig.Config, name string) (host *localconfig.Host) {
|
func hostOrTemplate(cfg *localconfig.Config, name string) (host *localconfig.Host) {
|
||||||
host = cfg.Host(name)
|
host = cfg.Host(name)
|
||||||
if host != nil {
|
if host != nil {
|
||||||
log.Print("no host named ", name)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,13 +38,13 @@ func hostOrTemplate(cfg *localconfig.Config, name string) (host *localconfig.Hos
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
log.Print("no host from template named ", name)
|
log.Print("no host named ", name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ht := cfg.HostTemplate(hft.Template)
|
ht := cfg.HostTemplate(hft.Template)
|
||||||
if ht == nil {
|
if ht == nil {
|
||||||
log.Print("no host template named ", name)
|
log.Print("host ", name, " found but no template named ", hft.Template)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,10 +37,13 @@ func registerWS(rest *restful.Container) {
|
|||||||
Route(ws.POST("/store.tar").To(wsStoreUpload).
|
Route(ws.POST("/store.tar").To(wsStoreUpload).
|
||||||
Consumes(mime.TAR).
|
Consumes(mime.TAR).
|
||||||
Doc("Upload an existing store")).
|
Doc("Upload an existing store")).
|
||||||
Route(ws.GET("/downloads/{token}/{asset}").To(wsDownload).
|
Route(ws.GET("/downloads/{token}").To(wsDownload)).
|
||||||
|
Route(ws.GET("/downloads/{token}/{asset}").To(wsDownloadAsset).
|
||||||
Param(ws.PathParameter("token", "the download token")).
|
Param(ws.PathParameter("token", "the download token")).
|
||||||
Param(ws.PathParameter("asset", "the requested asset")).
|
Param(ws.PathParameter("asset", "the requested asset")).
|
||||||
Doc("Fetch an asset via a download token"))
|
Doc("Fetch an asset via a download token")).
|
||||||
|
Route(ws.GET("/download-set").To(wsDownloadSet)).
|
||||||
|
Route(ws.GET("/download-set/{kind}/{name}/{asset}").To(wsDownloadSetAsset))
|
||||||
|
|
||||||
rest.Add(ws)
|
rest.Add(ws)
|
||||||
}
|
}
|
||||||
@ -65,6 +68,10 @@ func registerWS(rest *restful.Container) {
|
|||||||
Consumes(mime.JSON).Reads(DownloadSpec{}).
|
Consumes(mime.JSON).Reads(DownloadSpec{}).
|
||||||
Produces(mime.JSON).
|
Produces(mime.JSON).
|
||||||
Doc("Create a download token for the given download"))
|
Doc("Create a download token for the given download"))
|
||||||
|
ws.Route(ws.POST("/sign-download-set").To(wsSignDownloadSet).
|
||||||
|
Consumes(mime.JSON).Reads(DownloadSetReq{}).
|
||||||
|
Produces(mime.JSON).
|
||||||
|
Doc("Sign a download set"))
|
||||||
|
|
||||||
// - configs API
|
// - configs API
|
||||||
ws.Route(ws.POST("/configs").To(wsUploadConfig).
|
ws.Route(ws.POST("/configs").To(wsUploadConfig).
|
||||||
@ -88,6 +95,7 @@ func registerWS(rest *restful.Container) {
|
|||||||
const (
|
const (
|
||||||
GET = http.MethodGet
|
GET = http.MethodGet
|
||||||
PUT = http.MethodPut
|
PUT = http.MethodPut
|
||||||
|
POST = http.MethodPost
|
||||||
)
|
)
|
||||||
|
|
||||||
cluster := func(method, subPath string) *restful.RouteBuilder {
|
cluster := func(method, subPath string) *restful.RouteBuilder {
|
||||||
@ -126,6 +134,16 @@ func registerWS(rest *restful.Container) {
|
|||||||
Produces(mime.CERT).
|
Produces(mime.CERT).
|
||||||
Param(ws.QueryParameter("name", "signed reference name").Required(true)).
|
Param(ws.QueryParameter("name", "signed reference name").Required(true)).
|
||||||
Doc("Get cluster's certificate signed by the CA"),
|
Doc("Get cluster's certificate signed by the CA"),
|
||||||
|
|
||||||
|
cluster(GET, "/ssh/user-ca").To(wsClusterSSHUserCAPubKey).
|
||||||
|
Produces(mime.OCTET).
|
||||||
|
Doc("User CA public key for this cluster"),
|
||||||
|
cluster(POST, "/ssh/user-ca/sign").To(wsClusterSSHUserCASign).
|
||||||
|
Produces(mime.OCTET).
|
||||||
|
Doc("Sign a user's SSH public key for this cluster"),
|
||||||
|
cluster(POST, "/kube/sign").To(wsClusterKubeCASign).
|
||||||
|
Produces(mime.OCTET).
|
||||||
|
Doc("Sign a user's public key for this cluster's Kubernetes API server"),
|
||||||
} {
|
} {
|
||||||
ws.Route(builder)
|
ws.Route(builder)
|
||||||
}
|
}
|
||||||
|
71
go.mod
71
go.mod
@ -1,6 +1,8 @@
|
|||||||
module novit.tech/direktil/local-server
|
module novit.tech/direktil/local-server
|
||||||
|
|
||||||
go 1.21
|
go 1.24.0
|
||||||
|
|
||||||
|
toolchain go1.24.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cespare/xxhash v1.1.0
|
github.com/cespare/xxhash v1.1.0
|
||||||
@ -8,74 +10,73 @@ require (
|
|||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/emicklei/go-restful v2.16.0+incompatible
|
github.com/emicklei/go-restful v2.16.0+incompatible
|
||||||
github.com/emicklei/go-restful-openapi v1.4.1
|
github.com/emicklei/go-restful-openapi v1.4.1
|
||||||
github.com/go-git/go-git/v5 v5.12.0
|
github.com/go-git/go-git/v5 v5.16.2
|
||||||
github.com/mcluseau/go-swagger-ui v0.0.0-20191019002626-fd9128c24a34
|
github.com/mcluseau/go-swagger-ui v0.0.0-20191019002626-fd9128c24a34
|
||||||
github.com/miolini/datacounter v1.0.3
|
github.com/miolini/datacounter v1.0.3
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
github.com/pierrec/lz4 v2.6.1+incompatible
|
github.com/pierrec/lz4 v2.6.1+incompatible
|
||||||
github.com/sergeymakinen/go-crypt v1.0.0
|
github.com/sergeymakinen/go-crypt v1.0.1
|
||||||
golang.org/x/crypto v0.22.0
|
golang.org/x/crypto v0.39.0
|
||||||
gopkg.in/src-d/go-billy.v4 v4.3.2
|
gopkg.in/src-d/go-billy.v4 v4.3.2
|
||||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
k8s.io/apimachinery v0.29.3
|
k8s.io/apimachinery v0.33.2
|
||||||
m.cluseau.fr/go v0.0.0-20230809064045-12c5a121c766
|
m.cluseau.fr/go v0.0.0-20230809064045-12c5a121c766
|
||||||
novit.tech/direktil/pkg v0.0.0-20240415130406-0d2e181a4ed6
|
novit.tech/direktil/pkg v0.0.0-20250706092353-d857af8032a1
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/zmap/zlint/v3 => github.com/zmap/zlint/v3 v3.3.1
|
replace github.com/zmap/zlint/v3 => github.com/zmap/zlint/v3 v3.3.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||||
github.com/cavaliergopher/cpio v1.0.1 // indirect
|
github.com/cavaliergopher/cpio v1.0.1 // indirect
|
||||||
github.com/cloudflare/circl v1.3.7 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/frankban/quicktest v1.5.0 // indirect
|
github.com/frankban/quicktest v1.5.0 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
github.com/go-openapi/spec v0.21.0 // indirect
|
github.com/go-openapi/spec v0.21.0 // indirect
|
||||||
github.com/go-openapi/swag v0.23.0 // indirect
|
github.com/go-openapi/swag v0.23.1 // indirect
|
||||||
github.com/gobuffalo/envy v1.10.2 // indirect
|
github.com/gobuffalo/envy v1.10.2 // indirect
|
||||||
github.com/gobuffalo/packd v1.0.2 // indirect
|
github.com/gobuffalo/packd v1.0.2 // indirect
|
||||||
github.com/gobuffalo/packr v1.30.1 // indirect
|
github.com/gobuffalo/packr v1.30.1 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/google/certificate-transparency-go v1.1.8 // indirect
|
github.com/google/certificate-transparency-go v1.3.2 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
github.com/kisielk/sqlstruct v0.0.0-20210630145711-dae28ed37023 // indirect
|
github.com/kisielk/sqlstruct v0.0.0-20210630145711-dae28ed37023 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // 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/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.4.0 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||||
github.com/src-d/gcfg v1.4.0 // indirect
|
github.com/src-d/gcfg v1.4.0 // indirect
|
||||||
github.com/weppos/publicsuffix-go v0.30.2 // indirect
|
github.com/weppos/publicsuffix-go v0.40.3-0.20250617082559-9b2e24a9e482 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c // indirect
|
github.com/zmap/zcrypto v0.0.0-20250627161936-38850a079d72 // indirect
|
||||||
github.com/zmap/zlint/v3 v3.5.0 // indirect
|
github.com/zmap/zlint/v3 v3.5.0 // indirect
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
golang.org/x/mod v0.25.0 // indirect
|
||||||
golang.org/x/net v0.24.0 // indirect
|
golang.org/x/net v0.41.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/sys v0.19.0 // indirect
|
golang.org/x/text v0.26.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
|
||||||
golang.org/x/tools v0.20.0 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/klog/v2 v2.120.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||||
)
|
)
|
||||||
|
306
go.sum
306
go.sum
@ -1,15 +1,17 @@
|
|||||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
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/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.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
|
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||||
@ -20,35 +22,30 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW
|
|||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
|
||||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
|
||||||
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/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cloudflare/cfssl v1.6.4 h1:NMOvfrEjFfC63K3SGXgAnFdsgkmiq4kATme5BfcqrO8=
|
|
||||||
github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmDXacb+1J0=
|
|
||||||
github.com/cloudflare/cfssl v1.6.5 h1:46zpNkm6dlNkMZH/wMW22ejih6gIaJbzL2du6vD7ZeI=
|
github.com/cloudflare/cfssl v1.6.5 h1:46zpNkm6dlNkMZH/wMW22ejih6gIaJbzL2du6vD7ZeI=
|
||||||
github.com/cloudflare/cfssl v1.6.5/go.mod h1:Bk1si7sq8h2+yVEDrFJiz3d7Aw+pfjjJSZVaD+Taky4=
|
github.com/cloudflare/cfssl v1.6.5/go.mod h1:Bk1si7sq8h2+yVEDrFJiz3d7Aw+pfjjJSZVaD+Taky4=
|
||||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
|
||||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||||
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/emicklei/go-restful v2.16.0+incompatible h1:rgqiKNjTnFQA6kkhFe16D8epTksy9HQ1MyrbDXSdYhM=
|
github.com/emicklei/go-restful v2.16.0+incompatible h1:rgqiKNjTnFQA6kkhFe16D8epTksy9HQ1MyrbDXSdYhM=
|
||||||
github.com/emicklei/go-restful v2.16.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v2.16.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
@ -57,51 +54,40 @@ github.com/emicklei/go-restful-openapi v1.4.1/go.mod h1:kWQ8rQMVQ6G6lePwjDveJ00K
|
|||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
|
||||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/frankban/quicktest v1.5.0 h1:Tb4jWdSpdjKzTUicPnY61PZxKbDoGa7ABbrReT3gQVY=
|
github.com/frankban/quicktest v1.5.0 h1:Tb4jWdSpdjKzTUicPnY61PZxKbDoGa7ABbrReT3gQVY=
|
||||||
github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||||
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
|
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||||
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
|
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
|
||||||
github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
|
||||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
|
||||||
github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||||
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
|
|
||||||
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
|
|
||||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||||
github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||||
github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do=
|
|
||||||
github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw=
|
|
||||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||||
github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||||
github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
|
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||||
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
|
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
|
||||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||||
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
||||||
github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4=
|
github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4=
|
||||||
@ -114,33 +100,22 @@ github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wK
|
|||||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/google/certificate-transparency-go v1.1.7 h1:IASD+NtgSTJLPdzkthwvAG1ZVbF2WtFg4IvoA68XGSw=
|
|
||||||
github.com/google/certificate-transparency-go v1.1.7/go.mod h1:FSSBo8fyMVgqptbfF6j5p/XNdgQftAhSmXcIxV9iphE=
|
|
||||||
github.com/google/certificate-transparency-go v1.1.8 h1:LGYKkgZF7satzgTak9R4yzfJXEeYVAjV6/EAEJOf1to=
|
|
||||||
github.com/google/certificate-transparency-go v1.1.8/go.mod h1:bV/o8r0TBKRf1X//iiiSgWrvII4d7/8OiA+3vG26gI8=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
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/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q=
|
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
@ -155,6 +130,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
|
|||||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kisielk/sqlstruct v0.0.0-20210630145711-dae28ed37023 h1:/pb3UJ+3ZtSEUKWnufwsoVF7f0AX5ytPULbTwHMgbq4=
|
github.com/kisielk/sqlstruct v0.0.0-20210630145711-dae28ed37023 h1:/pb3UJ+3ZtSEUKWnufwsoVF7f0AX5ytPULbTwHMgbq4=
|
||||||
github.com/kisielk/sqlstruct v0.0.0-20210630145711-dae28ed37023/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
github.com/kisielk/sqlstruct v0.0.0-20210630145711-dae28ed37023/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@ -165,21 +142,19 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
|||||||
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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
|
||||||
github.com/mcluseau/go-swagger-ui v0.0.0-20191019002626-fd9128c24a34 h1:F3u4DKQ4T30mlBNFmSGzTqdkmVqbfVORv34ZRvc7PuE=
|
github.com/mcluseau/go-swagger-ui v0.0.0-20191019002626-fd9128c24a34 h1:F3u4DKQ4T30mlBNFmSGzTqdkmVqbfVORv34ZRvc7PuE=
|
||||||
github.com/mcluseau/go-swagger-ui v0.0.0-20191019002626-fd9128c24a34/go.mod h1:lcyE8C83VRamH/oTpikU4+yVCCxLthWgDOqjHSsu+ZY=
|
github.com/mcluseau/go-swagger-ui v0.0.0-20191019002626-fd9128c24a34/go.mod h1:lcyE8C83VRamH/oTpikU4+yVCCxLthWgDOqjHSsu+ZY=
|
||||||
github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA=
|
github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA=
|
||||||
@ -192,51 +167,42 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
|
|
||||||
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
|
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
|
||||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
|
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
|
||||||
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sergeymakinen/go-crypt v1.0.0 h1:R9yyekVU0bxP7MUUJd3uCcGQxg5RqMJ/ahy2bllx9qg=
|
github.com/sergeymakinen/go-crypt v1.0.1 h1:InrJqCaOzWHN7Pv27V1c/MeW6ydQqdrDjU99dXHaoNY=
|
||||||
github.com/sergeymakinen/go-crypt v1.0.0/go.mod h1:5NPTCUNfQrNlck+I9ONHFT55UvGWzosSf+QQPSuxYBE=
|
github.com/sergeymakinen/go-crypt v1.0.1/go.mod h1:sTVy1KCTr+ot85sw2h2fMalv5QRD1cgIWqlBFI/8axg=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
|
||||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||||
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
|
|
||||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
|
||||||
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
|
|
||||||
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
@ -254,95 +220,52 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
github.com/weppos/publicsuffix-go v0.40.3-0.20250617082559-9b2e24a9e482 h1:0HudNf74HwwerH9HSlQYxfK+53VqFo6U04lQuTxfRf8=
|
||||||
github.com/weppos/publicsuffix-go v0.15.1-0.20220329081811-9a40b608a236/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
|
github.com/weppos/publicsuffix-go v0.40.3-0.20250617082559-9b2e24a9e482/go.mod h1:Efaen92I7hksG9EA+bsuHPWscS8ePs86CXxNFfG2cG4=
|
||||||
github.com/weppos/publicsuffix-go v0.30.2-0.20230730094716-a20f9abcc222 h1:h2JizvZl9aIj6za9S5AyrkU+OzIS4CetQthH/ejO+lg=
|
|
||||||
github.com/weppos/publicsuffix-go v0.30.2-0.20230730094716-a20f9abcc222/go.mod h1:s41lQh6dIsDWIC1OWh7ChWJXLH0zkJ9KHZVqA7vHyuQ=
|
|
||||||
github.com/weppos/publicsuffix-go v0.30.2 h1:Np18yzfMR90jNampWFs7iSh2sw/qCZkhL41/ffyihCU=
|
|
||||||
github.com/weppos/publicsuffix-go v0.30.2/go.mod h1:/hGscit36Yt+wammfBBwdMdxBT8btsTt6KvwO9OvMyM=
|
|
||||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
|
github.com/zmap/zcrypto v0.0.0-20250627161936-38850a079d72 h1:QcaEozNpjw8LcvzepkftwJ3kevF6qY7qRMikCvxWhpg=
|
||||||
github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
|
github.com/zmap/zcrypto v0.0.0-20250627161936-38850a079d72/go.mod h1:uvqhJWCdbMIHIXZSKcqnJYy0yR/9v/TON/JQFbM2g6Q=
|
||||||
github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
|
|
||||||
github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk=
|
|
||||||
github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ=
|
|
||||||
github.com/zmap/zcrypto v0.0.0-20220402174210-599ec18ecbac/go.mod h1:egdRkzUylATvPkWMpebZbXhv0FMEMJGX/ur0D3Csk2s=
|
|
||||||
github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c h1:U1b4THKcgOpJ+kILupuznNwPiURtwVW3e9alJvji9+s=
|
|
||||||
github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c/go.mod h1:GSDpFDD4TASObxvfZfvpZZ3OWHIUHMlhVWlkOe4ewVk=
|
|
||||||
github.com/zmap/zlint/v3 v3.3.1 h1:IrIY2Qd2Wr9ZHhdQ3mszehSydz+x6OROClztMEK+2bU=
|
github.com/zmap/zlint/v3 v3.3.1 h1:IrIY2Qd2Wr9ZHhdQ3mszehSydz+x6OROClztMEK+2bU=
|
||||||
github.com/zmap/zlint/v3 v3.3.1/go.mod h1:fPCW5acxhqw4HU1Vm0t9oFEPo1/uH9hI0sci/Z++hEI=
|
github.com/zmap/zlint/v3 v3.3.1/go.mod h1:fPCW5acxhqw4HU1Vm0t9oFEPo1/uH9hI0sci/Z++hEI=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
|
||||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
|
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||||
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
|
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
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/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
|
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
|
||||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -351,81 +274,49 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
|
||||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
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.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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
|
|
||||||
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
|
|
||||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
|
||||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
|
||||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
|
||||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
|
||||||
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@ -442,27 +333,18 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
|||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
|
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||||
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
|
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||||
k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU=
|
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||||
k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU=
|
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ=
|
|
||||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
|
||||||
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY=
|
|
||||||
k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
|
||||||
m.cluseau.fr/go v0.0.0-20230809064045-12c5a121c766 h1:JRzMBDbUwrTTGDJaJSH0ap4vRL0Q9CN1bG8a6n49eaQ=
|
m.cluseau.fr/go v0.0.0-20230809064045-12c5a121c766 h1:JRzMBDbUwrTTGDJaJSH0ap4vRL0Q9CN1bG8a6n49eaQ=
|
||||||
m.cluseau.fr/go v0.0.0-20230809064045-12c5a121c766/go.mod h1:BMv3aOSYpupuiiG3Ch3ND88aB5CfAks3YZuRLE8j1ls=
|
m.cluseau.fr/go v0.0.0-20230809064045-12c5a121c766/go.mod h1:BMv3aOSYpupuiiG3Ch3ND88aB5CfAks3YZuRLE8j1ls=
|
||||||
novit.tech/direktil/pkg v0.0.0-20240120172717-8498a102796f h1:7y11nLhChrrsLQwRaW7wn/9x+Xn2gEVtzj75VkOpJ+o=
|
novit.tech/direktil/pkg v0.0.0-20250706092353-d857af8032a1 h1:hKj9qhbTAoTxYIj6KaMLJp9I+bvZfkSM/QwK8Bd496o=
|
||||||
novit.tech/direktil/pkg v0.0.0-20240120172717-8498a102796f/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10=
|
novit.tech/direktil/pkg v0.0.0-20250706092353-d857af8032a1/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10=
|
||||||
novit.tech/direktil/pkg v0.0.0-20240415114750-ccdbf2e2a5e4 h1:8HviBvbPzIBo2VpDl2jc6iPwiqlOrAbXqRhtn/yYMYE=
|
|
||||||
novit.tech/direktil/pkg v0.0.0-20240415114750-ccdbf2e2a5e4/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10=
|
|
||||||
novit.tech/direktil/pkg v0.0.0-20240415130406-0d2e181a4ed6 h1:D0TN5GyZ4d88ILpgVZgcZ62027lW8/LLnQSpQyN2yOw=
|
|
||||||
novit.tech/direktil/pkg v0.0.0-20240415130406-0d2e181a4ed6/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10=
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
|
GIT_TAG=$(git describe --always --dirty)
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
commit) tag=$(git describe --always --dirty) ;;
|
commit) tag=$GIT_TAG ;;
|
||||||
"") tag=latest ;;
|
"") tag=latest ;;
|
||||||
*) tag=$1 ;;
|
*) tag=$1 ;;
|
||||||
esac
|
esac
|
||||||
docker build -t novit.tech/direktil/local-server:$tag .
|
docker build -t novit.tech/direktil/local-server:$tag . --build-arg GIT_TAG=$GIT_TAG
|
||||||
|
@ -1,23 +1,29 @@
|
|||||||
|
|
||||||
.downloads {
|
.view-links > span {
|
||||||
display: flex;
|
display: inline-block;
|
||||||
align-content: stretch;
|
white-space: nowrap;
|
||||||
|
margin-right: 1ex;
|
||||||
|
margin-bottom: 1ex;
|
||||||
|
padding: 0.5ex;
|
||||||
|
border: 1pt solid;
|
||||||
|
border-radius: 1ex;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.downloads > * {
|
.downloads, .download-links {
|
||||||
margin-left: 6pt;
|
& > * {
|
||||||
}
|
display: inline-block;
|
||||||
.downloads > *:first-child {
|
margin-right: 1ex;
|
||||||
margin-left: 0;
|
margin-bottom: 1ex;
|
||||||
|
padding: 0.5ex;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 1ex;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.downloads > div {
|
.downloads, .view-links {
|
||||||
display: flex;
|
& > .selected {
|
||||||
flex-flow: column;
|
color: var(--link);
|
||||||
max-height: 100pt;
|
}
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cluster {
|
|
||||||
max-width: 50%;
|
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
<template v-if="!publicState">
|
<template v-if="!publicState">
|
||||||
<p>Not connected.</p>
|
<p>Not connected.</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="publicState.Store.New">
|
<template v-else-if="publicState.Store.New">
|
||||||
<p>Store is new.</p>
|
<p>Store is new.</p>
|
||||||
<p>Option 1: initialize a new store</p>
|
<p>Option 1: initialize a new store</p>
|
||||||
@ -53,6 +54,7 @@
|
|||||||
<input type="submit" value="upload" />
|
<input type="submit" value="upload" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="!publicState.Store.Open">
|
<template v-else-if="!publicState.Store.Open">
|
||||||
<p>Store is not open.</p>
|
<p>Store is not open.</p>
|
||||||
<form @submit="unlockStore">
|
<form @submit="unlockStore">
|
||||||
@ -60,6 +62,7 @@
|
|||||||
<input type="submit" value="unlock" :disabled="!forms.store.pass1" />
|
<input type="submit" value="unlock" :disabled="!forms.store.pass1" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="!state">
|
<template v-else-if="!state">
|
||||||
<p v-if="!session.token">Not logged in.</p>
|
<p v-if="!session.token">Not logged in.</p>
|
||||||
<p v-else>Invalid token</p>
|
<p v-else>Invalid token</p>
|
||||||
@ -69,25 +72,22 @@
|
|||||||
<input type="submit" value="log in"/>
|
<input type="submit" value="log in"/>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div v-if="state.Clusters" id="clusters">
|
<div style="float:right;"><input type="search" placeholder="Filter" v-model="viewFilter"/></div>
|
||||||
<h2>Clusters</h2>
|
<p class="view-links"><span v-for="v in views" @click="view = v" :class="{selected: view.type==v.type && view.name==v.name}">{{v.title}}</span></p>
|
||||||
|
|
||||||
<div class="sheets">
|
<h2 v-if="view">{{view.title}}</h2>
|
||||||
<Cluster v-for="c in state.Clusters" :cluster="c" :token="session.token" :state="state" />
|
|
||||||
</div>
|
<div v-if="view.type == 'cluster'" id="clusters">
|
||||||
|
<Cluster :cluster="viewObj" :token="session.token" :state="state" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="state.Hosts" id="hosts">
|
<div v-if="view.type == 'host'" id="hosts">
|
||||||
<h2>Hosts</h2>
|
<Host :host="viewObj" :token="session.token" :state="state" />
|
||||||
|
|
||||||
<div class="sheets">
|
|
||||||
<Host v-for="h in state.Hosts" :host="h" :token="session.token" :state="state" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Admin actions</h2>
|
<div v-if="view.type == 'actions' && view.name == 'admin'">
|
||||||
|
|
||||||
<h3>Config</h3>
|
<h3>Config</h3>
|
||||||
<form @submit="uploadConfig">
|
<form @submit="uploadConfig">
|
||||||
<input type="file" ref="configUpload" required />
|
<input type="file" ref="configUpload" required />
|
||||||
@ -112,9 +112,9 @@
|
|||||||
<template v-for="k,i in state.Store.KeyNames">{{i?", ":""}}<code @click="forms.delKey.name=k">{{k}}</code></template>.</p>
|
<template v-for="k,i in state.Store.KeyNames">{{i?", ":""}}<code @click="forms.delKey.name=k">{{k}}</code></template>.</p>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<template v-if="state.HostTemplates && state.HostTemplates.length">
|
<template v-if="any(state.HostTemplates) || any(hostsFromTemplate)">
|
||||||
<h3>Hosts from template</h3>
|
<h3>Hosts from template</h3>
|
||||||
<form @submit="hostFromTemplateAdd" action="">
|
<form @submit="hostFromTemplateAdd" action="" v-if="any(state.HostTemplates)">
|
||||||
<p>Add a host from template instance:</p>
|
<p>Add a host from template instance:</p>
|
||||||
<input type="text" v-model="forms.hostFromTemplate.name" required placeholder="Name" />
|
<input type="text" v-model="forms.hostFromTemplate.name" required placeholder="Name" />
|
||||||
<select v-model="forms.hostFromTemplate.Template" required>
|
<select v-model="forms.hostFromTemplate.Template" required>
|
||||||
@ -123,7 +123,7 @@
|
|||||||
<input type="text" v-model="forms.hostFromTemplate.IP" required placeholder="IP" />
|
<input type="text" v-model="forms.hostFromTemplate.IP" required placeholder="IP" />
|
||||||
<input type="submit" value="add instance" />
|
<input type="submit" value="add instance" />
|
||||||
</form>
|
</form>
|
||||||
<form @submit="hostFromTemplateDel" action="">
|
<form @submit="hostFromTemplateDel" action="" v-if="any(hostsFromTemplate)">
|
||||||
<p>Remove a host from template instance:</p>
|
<p>Remove a host from template instance:</p>
|
||||||
<select v-model="forms.hostFromTemplateDel" required>
|
<select v-model="forms.hostFromTemplateDel" required>
|
||||||
<option v-for="h in hostsFromTemplate" :value="h.Name">{{h.Name}}</option>
|
<option v-for="h in hostsFromTemplate" :value="h.Name">{{h.Name}}</option>
|
||||||
@ -131,7 +131,7 @@
|
|||||||
<input type="submit" value="delete instance" :disabled="!forms.hostFromTemplateDel" />
|
<input type="submit" value="delete instance" :disabled="!forms.hostFromTemplateDel" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -5,22 +5,59 @@ import GetCopy from './GetCopy.js';
|
|||||||
export default {
|
export default {
|
||||||
components: { Downloads, GetCopy },
|
components: { Downloads, GetCopy },
|
||||||
props: [ 'cluster', 'token', 'state' ],
|
props: [ 'cluster', 'token', 'state' ],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
signReqValidity: "1d",
|
||||||
|
sshSignReq: {
|
||||||
|
PubKey: "",
|
||||||
|
Principal: "root",
|
||||||
|
},
|
||||||
|
sshUserCert: null,
|
||||||
|
kubeSignReq: {
|
||||||
|
CSR: "",
|
||||||
|
User: "anonymous",
|
||||||
|
Group: "",
|
||||||
|
},
|
||||||
|
kubeUserCert: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sshCASign() {
|
||||||
|
event.preventDefault();
|
||||||
|
fetch(`/clusters/${this.cluster.Name}/ssh/user-ca/sign`, {
|
||||||
|
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) })
|
||||||
|
.catch((e) => { alert('failed to sign: '+e); })
|
||||||
|
},
|
||||||
|
kubeCASign() {
|
||||||
|
event.preventDefault();
|
||||||
|
fetch(`/clusters/${this.cluster.Name}/kube/sign`, {
|
||||||
|
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) })
|
||||||
|
.catch((e) => { alert('failed to sign: '+e); })
|
||||||
|
},
|
||||||
|
},
|
||||||
template: `
|
template: `
|
||||||
<div class="cluster">
|
<h3>Tokens</h3>
|
||||||
<div class="title">Cluster {{ cluster.Name }}</div>
|
|
||||||
<div class="section">Tokens</div>
|
|
||||||
<section class="links">
|
<section class="links">
|
||||||
<GetCopy v-for="n in cluster.Tokens" :token="token" :name="n" :href="'/clusters/'+cluster.Name+'/tokens/'+n" />
|
<GetCopy v-for="n in cluster.Tokens" :token="token" :name="n" :href="'/clusters/'+cluster.Name+'/tokens/'+n" />
|
||||||
</section>
|
</section>
|
||||||
<div class="section">Passwords</div>
|
|
||||||
|
<h3>Passwords</h3>
|
||||||
<section class="links">
|
<section class="links">
|
||||||
<GetCopy v-for="n in cluster.Passwords" :token="token" :name="n" :href="'/clusters/'+cluster.Name+'/passwords/'+n" />
|
<GetCopy v-for="n in cluster.Passwords" :token="token" :name="n" :href="'/clusters/'+cluster.Name+'/passwords/'+n" />
|
||||||
</section>
|
</section>
|
||||||
<div class="section">Downloads</div>
|
|
||||||
<section class="downloads">
|
<h3>Downloads</h3>
|
||||||
<Downloads :token="token" :state="state" kind="cluster" :name="cluster.Name" />
|
<Downloads :token="token" :state="state" kind="cluster" :name="cluster.Name" />
|
||||||
</section>
|
|
||||||
<div class="section">CAs</div>
|
<h3>CAs</h3>
|
||||||
<table><tr><th>Name</th><th>Certificate</th><th>Signed certificates</th></tr>
|
<table><tr><th>Name</th><th>Certificate</th><th>Signed certificates</th></tr>
|
||||||
<tr v-for="ca in cluster.CAs">
|
<tr v-for="ca in cluster.CAs">
|
||||||
<td>{{ ca.Name }}</td>
|
<td>{{ ca.Name }}</td>
|
||||||
@ -30,6 +67,35 @@ export default {
|
|||||||
<GetCopy :token="token" :name="signed" :href="'/clusters/'+cluster.Name+'/CAs/'+ca.Name+'/signed?name='+signed" />
|
<GetCopy :token="token" :name="signed" :href="'/clusters/'+cluster.Name+'/CAs/'+ca.Name+'/signed?name='+signed" />
|
||||||
</template></td>
|
</template></td>
|
||||||
</tr></table>
|
</tr></table>
|
||||||
</div>
|
|
||||||
|
<h3>Access</h3>
|
||||||
|
|
||||||
|
<p>Allow cluster access from a public key</p>
|
||||||
|
<p>Certificate time validity: <input type="text" v-model="signReqValidity"/> <small>ie: -5m:1w, 5m, 1M, 1y, 1d-1s, etc.</p>
|
||||||
|
|
||||||
|
<h4>Grant SSH access</h4>
|
||||||
|
|
||||||
|
<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><button @click="sshCASign">Sign SSH access request</button></p>
|
||||||
|
<p v-if="sshUserCert">
|
||||||
|
<a :href="sshUserCert" download="ssh-cert.pub">Get certificate</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>Grant Kubernetes API access</h4>
|
||||||
|
|
||||||
|
<p>Certificate signing request (PEM format):<br/>
|
||||||
|
<textarea v-model="kubeSignReq.CSR" style="width:64em;height:7lh;"></textarea>
|
||||||
|
</p>
|
||||||
|
<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>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
@ -13,30 +13,29 @@ export default {
|
|||||||
"initrd",
|
"initrd",
|
||||||
"bootstrap.tar",
|
"bootstrap.tar",
|
||||||
"boot.img.lz4",
|
"boot.img.lz4",
|
||||||
"boot.iso",
|
|
||||||
"config",
|
|
||||||
"bootstrap-config",
|
|
||||||
"boot.tar",
|
|
||||||
"boot-efi.tar",
|
|
||||||
"boot.img.gz",
|
"boot.img.gz",
|
||||||
"boot.img",
|
|
||||||
"boot.qcow2",
|
"boot.qcow2",
|
||||||
"boot.vmdk",
|
"boot.vmdk",
|
||||||
|
"boot.img",
|
||||||
|
"boot.iso",
|
||||||
|
"boot.tar",
|
||||||
|
"boot-efi.tar",
|
||||||
|
"config",
|
||||||
|
"bootstrap-config",
|
||||||
"ipxe",
|
"ipxe",
|
||||||
],
|
],
|
||||||
}[this.kind]
|
}[this.kind]
|
||||||
},
|
},
|
||||||
downloads() {
|
downloads() {
|
||||||
let ret = []
|
return Object.entries(this.state.Downloads)
|
||||||
Object.entries(this.state.Downloads)
|
|
||||||
.filter(e => { let d=e[1]; return d.Kind == this.kind && d.Name == this.name })
|
.filter(e => { let d=e[1]; return d.Kind == this.kind && d.Name == this.name })
|
||||||
.forEach(e => {
|
.map(e => {
|
||||||
let token= e[0], d = e[1]
|
const token= e[0];
|
||||||
d.Assets.forEach(asset => {
|
return {
|
||||||
ret.push({name: asset, url: '/public/downloads/'+token+'/'+asset})
|
text: token.substring(0, 5) + '...',
|
||||||
|
url: '/public/downloads/'+token+"/",
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
|
||||||
return ret
|
|
||||||
},
|
},
|
||||||
assets() {
|
assets() {
|
||||||
return this.availableAssets.filter(a => this.selectedAssets[a])
|
return this.availableAssets.filter(a => this.selectedAssets[a])
|
||||||
@ -56,11 +55,17 @@ export default {
|
|||||||
.catch((e) => { alert('failed to create link'); this.createDisabled = false })
|
.catch((e) => { alert('failed to create link'); this.createDisabled = false })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
template: `<div class="downloads">
|
template: `
|
||||||
<div class="options">
|
<h4>Available assets</h4>
|
||||||
<span v-for="asset in availableAssets"><label><input type="checkbox" v-model="selectedAssets[asset]" /> {{ asset }}</label></span>
|
<p class="downloads">
|
||||||
</div>
|
<template v-for="asset in availableAssets">
|
||||||
<button :disabled="createDisabled || assets.length==0" @click="createToken">+</button>
|
<label :class="{selected: selectedAssets[asset]}"><input type="checkbox" v-model="selectedAssets[asset]" /> {{ asset }}</label>
|
||||||
<div><a v-for="d in downloads" target="_blank" :href="d.url">{{ d.name }}</a></div>
|
{{" "}}
|
||||||
</div>`
|
</template>
|
||||||
|
</p>
|
||||||
|
<p><button :disabled="createDisabled || assets.length==0" @click="createToken">Create link</button></p>
|
||||||
|
<template v-if="downloads.length">
|
||||||
|
<h4>Active links</h4>
|
||||||
|
<p class="download-links"><template v-for="d in downloads"><a :href="d.url" target="_blank">{{ d.text }}</a>{{" "}}</template></p>
|
||||||
|
</template>`
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,13 @@ export default {
|
|||||||
components: { Downloads },
|
components: { Downloads },
|
||||||
props: [ 'host', 'token', 'state' ],
|
props: [ 'host', 'token', 'state' ],
|
||||||
template: `
|
template: `
|
||||||
<div class="host">
|
<p>Cluster: {{ host.Cluster }}<template v-if="host.Template"> ({{ host.Template }})</template></p>
|
||||||
<div class="title">Host {{ host.Name }}</div>
|
<p>IPs:
|
||||||
<section>
|
<code v-for="ip in host.IPs">
|
||||||
<div><small>Cluster: {{ host.Cluster }}<template v-if="host.Template"> ({{ host.Template }})</template></small></div>
|
{{ ip }}{{" "}}
|
||||||
<template v-for="ip in host.IPs">
|
</code>
|
||||||
<code>{{ ip }}</code>{{" "}}
|
</p>
|
||||||
</template>
|
<h3>Downloads</h3>
|
||||||
</section>
|
|
||||||
<div class="section">Downloads</div>
|
|
||||||
<section>
|
|
||||||
<Downloads :token="token" :state="state" kind="host" :name="host.Name" />
|
<Downloads :token="token" :state="state" kind="host" :name="host.Name" />
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ createApp({
|
|||||||
hostFromTemplate: {},
|
hostFromTemplate: {},
|
||||||
hostFromTemplateDel: "",
|
hostFromTemplateDel: "",
|
||||||
},
|
},
|
||||||
|
view: "",
|
||||||
|
viewFilter: "",
|
||||||
session: {},
|
session: {},
|
||||||
error: null,
|
error: null,
|
||||||
publicState: null,
|
publicState: null,
|
||||||
@ -57,12 +59,34 @@ createApp({
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
views() {
|
||||||
|
var views = [{type: "actions", name: "admin", title: "Admin actions"}];
|
||||||
|
|
||||||
|
(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));
|
||||||
|
},
|
||||||
|
viewObj() {
|
||||||
|
if (this.view) {
|
||||||
|
if (this.view.type == "cluster") {
|
||||||
|
return this.state.Clusters.find((c) => c.Name == this.view.name);
|
||||||
|
}
|
||||||
|
if (this.view.type == "host") {
|
||||||
|
return this.state.Hosts.find((h) => h.Name == this.view.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
hostsFromTemplate() {
|
hostsFromTemplate() {
|
||||||
return (this.state.Hosts||[]).filter((h) => h.Template)
|
return (this.state.Hosts||[]).filter((h) => h.Template);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
any(array) {
|
||||||
|
return array && array.length != 0;
|
||||||
|
},
|
||||||
copyText(text) {
|
copyText(text) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
window.navigator.clipboard.writeText(text)
|
window.navigator.clipboard.writeText(text)
|
||||||
|
@ -1,5 +1,30 @@
|
|||||||
|
:root {
|
||||||
|
--bg: #eee;
|
||||||
|
--color: black;
|
||||||
|
--bevel-dark: darkgray;
|
||||||
|
--bevel-light: lightgray;
|
||||||
|
--link: blue;
|
||||||
|
--input-bg: #ddd;
|
||||||
|
--input-text: white;
|
||||||
|
--btn-bg: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--bg: black;
|
||||||
|
--color: orange;
|
||||||
|
--bevel-dark: #402900;
|
||||||
|
--bevel-light: #805300;
|
||||||
|
--link: #31b0fa;
|
||||||
|
--input-bg: #111;
|
||||||
|
--input-text: #ddd;
|
||||||
|
--btn-bg: #222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: white;
|
background: var(--bg);
|
||||||
|
color: var(--color);
|
||||||
}
|
}
|
||||||
|
|
||||||
button[disabled] {
|
button[disabled] {
|
||||||
@ -8,7 +33,7 @@ button[disabled] {
|
|||||||
|
|
||||||
a[href], a[href]:visited, button.link {
|
a[href], a[href]:visited, button.link {
|
||||||
border: none;
|
border: none;
|
||||||
color: blue;
|
color: var(--link);
|
||||||
background: none;
|
background: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -37,20 +62,38 @@ th, tr:last-child > td {
|
|||||||
.red { color: red; }
|
.red { color: red; }
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
body {
|
.red { color: #c00; }
|
||||||
background: black;
|
}
|
||||||
color: orange;
|
|
||||||
|
textarea, select, input {
|
||||||
|
background: var(--input-bg);
|
||||||
|
color: var(--input-text);
|
||||||
|
border: solid 1pt;
|
||||||
|
border-color: var(--bevel-light);
|
||||||
|
border-top-color: var(--bevel-dark);
|
||||||
|
border-left-color: var(--bevel-dark);
|
||||||
|
margin: 1pt;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: solid 1pt var(--color);
|
||||||
}
|
}
|
||||||
button, input[type=submit] {
|
|
||||||
background: #333;
|
|
||||||
color: #eee;
|
|
||||||
}
|
|
||||||
a[href], a[href]:visited, button.link {
|
|
||||||
border: none;
|
|
||||||
color: #31b0fa;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.red { color: #c00; }
|
button, input[type=button], input[type=submit], ::file-selector-button {
|
||||||
|
background: var(--btn-bg);
|
||||||
|
color: var(--color);
|
||||||
|
border: solid 2pt;
|
||||||
|
border-color: var(--bevel-dark);
|
||||||
|
border-top-color: var(--bevel-light);
|
||||||
|
border-left-color: var(--bevel-light);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--bevel-dark);
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
background: var(--bevel-dark);
|
||||||
|
border-color: var(--bevel-light);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
|
14
modd.conf
14
modd.conf
@ -4,23 +4,9 @@ modd.conf {}
|
|||||||
prep: go test ./...
|
prep: go test ./...
|
||||||
prep: mkdir -p dist
|
prep: mkdir -p dist
|
||||||
prep: hack/build ./...
|
prep: hack/build ./...
|
||||||
#prep: docker build --build-arg GOPROXY=$GOPROXY -t dls .
|
|
||||||
#daemon +sigterm: bash test-run
|
#daemon +sigterm: bash test-run
|
||||||
}
|
}
|
||||||
|
|
||||||
html/**/* {
|
html/**/* {
|
||||||
prep: hack/build ./cmd/dkl-local-server
|
prep: hack/build ./cmd/dkl-local-server
|
||||||
}
|
}
|
||||||
|
|
||||||
#dist/dkl-local-server {
|
|
||||||
# prep: mkdir -p tmp
|
|
||||||
# daemon +sigterm: dist/dkl-local-server -data tmp -auto-unlock test
|
|
||||||
#}
|
|
||||||
|
|
||||||
dist/dkl-dir2config {
|
|
||||||
# prep: dist/dkl-dir2config --debug --in test-dir2config
|
|
||||||
}
|
|
||||||
|
|
||||||
#**/*.proto !dist/**/* {
|
|
||||||
# prep: for mod in @mods; do protoc -I ./ --go_out=plugins=grpc,paths=source_relative:. $mod; done
|
|
||||||
#}
|
|
||||||
|
Reference in New Issue
Block a user