Full web : fichiers restants
This commit is contained in:
1 parent
ec3b7c9a01
commit
4826fa5b82
26 files changed
+2537
-793
No files matched your search
+67
-41
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"pve-confsync/internal/engine"
|
||||
"pve-confsync/internal/model"
|
||||
"pve-confsync/internal/pve"
|
||||
"pve-confsync/internal/sshx"
|
||||
)
|
||||
|
||||
// Node is the PVE version of one node.
|
||||
@@ -255,6 +257,10 @@ func rows(cfg *config.Config, cl *config.Client, ref, actual []model.Object, pla
|
||||
case fd.Policy != adapter.Ignore && fd.Current != nil:
|
||||
fd.Target = fd.Current
|
||||
}
|
||||
if adapter.IsSensitive(k, f) { // password hashes: a digest only
|
||||
fd.Ref, fd.Mapping = adapter.Redact(fd.Ref), adapter.Redact(fd.Mapping)
|
||||
fd.Current, fd.Target = adapter.Redact(fd.Current), adapter.Redact(fd.Target)
|
||||
}
|
||||
row.Fields = append(row.Fields, fd)
|
||||
}
|
||||
out = append(out, row)
|
||||
@@ -355,67 +361,87 @@ func (st *State) Apply(ctx context.Context, cp ClientPlan, keys []string, log fu
|
||||
return err
|
||||
}
|
||||
|
||||
// Check tests the access to a cluster: versions (9.x only) and token privileges.
|
||||
// Check is the result of a connection test: API with the root credentials
|
||||
// (login, PVE 9.x on every node) and SSH as root on the node.
|
||||
type Check struct {
|
||||
Nodes []Node `json:"nodes,omitempty"`
|
||||
OK bool `json:"ok"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Warnings []string `json:"warnings,omitempty"`
|
||||
API bool `json:"api"`
|
||||
SSH bool `json:"ssh"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Nodes []Node `json:"nodes,omitempty"`
|
||||
// set when something is seen for the first time and must be recorded
|
||||
TLSFingerprint string `json:"tlsFingerprint,omitempty"`
|
||||
SSHHostKey string `json:"-"`
|
||||
SSHFingerprint string `json:"sshFingerprint,omitempty"`
|
||||
SSHMismatch bool `json:"sshMismatch,omitempty"` // the node presents another key than the recorded one
|
||||
}
|
||||
|
||||
// CheckCluster runs the same checks as the "check" command.
|
||||
func CheckCluster(ctx context.Context, cl config.Cluster, write, dcOptions bool) Check {
|
||||
// CheckCluster tests a cluster. pinTLS: the TLS mode pins the certificate and
|
||||
// none is recorded yet (trust on first use). trust accepts a new SSH host key.
|
||||
func CheckCluster(ctx context.Context, cl config.Cluster, pinTLS, trust bool) Check {
|
||||
var res Check
|
||||
c, err := cl.PVE()
|
||||
if err != nil {
|
||||
fail := func(err error) Check {
|
||||
res.Error = err.Error()
|
||||
return res
|
||||
}
|
||||
if pinTLS && cl.Fingerprint == "" {
|
||||
fp, err := pve.PeerFingerprint(ctx, cl.URL)
|
||||
if err != nil {
|
||||
return fail(fmt.Errorf("TLS: %w", err))
|
||||
}
|
||||
cl.Fingerprint, res.TLSFingerprint = fp, fp
|
||||
}
|
||||
if cl.Insecure {
|
||||
res.Warnings = append(res.Warnings, "certificat TLS non vérifié (labo uniquement)")
|
||||
}
|
||||
key := cl.SSHHostKey
|
||||
cl.SSHHostKey = "" // API only for now
|
||||
c, err := cl.PVE()
|
||||
if err != nil {
|
||||
return fail(err)
|
||||
}
|
||||
if err := c.Login(ctx); err != nil {
|
||||
return fail(fmt.Errorf("API: %w", err))
|
||||
}
|
||||
vs, err := c.CheckVersion(ctx)
|
||||
res.Nodes = nodes(vs)
|
||||
if err != nil {
|
||||
res.Error = err.Error()
|
||||
return res
|
||||
if len(vs) > 0 {
|
||||
res.Version = vs[0].Version
|
||||
}
|
||||
perms, err := c.Permissions(ctx)
|
||||
if err != nil {
|
||||
res.Warnings = append(res.Warnings, fmt.Sprintf("token privileges unreadable (%v)", err))
|
||||
return res
|
||||
return fail(err)
|
||||
}
|
||||
res.Warnings = PrivilegeWarnings(perms, write, dcOptions)
|
||||
return res
|
||||
}
|
||||
res.API = true
|
||||
|
||||
// PrivilegeWarnings flags a reference token able to write, and a client token
|
||||
// missing the privileges needed by the synchronized kinds.
|
||||
func PrivilegeWarnings(perms map[string]map[string]any, write, dcOptions bool) []string {
|
||||
has := func(path, priv string) bool { _, ok := perms[path][priv]; return ok }
|
||||
anyPath := func(priv string) bool {
|
||||
for p := range perms {
|
||||
if has(p, priv) {
|
||||
return true
|
||||
}
|
||||
o := cl.SSHOptions()
|
||||
if key == "" || trust {
|
||||
seen, fp, err := sshx.Scan(ctx, o)
|
||||
if err != nil {
|
||||
return fail(fmt.Errorf("SSH: %w", err))
|
||||
}
|
||||
return false
|
||||
key, res.SSHHostKey, res.SSHFingerprint = seen, seen, fp
|
||||
}
|
||||
var w []string
|
||||
if !write {
|
||||
for _, p := range []string{"Sys.Modify", "SDN.Allocate", "Permissions.Modify"} {
|
||||
if anyPath(p) {
|
||||
w = append(w, "le token de référence possède "+p+" : un token en lecture seule est attendu")
|
||||
}
|
||||
o.HostKey = key
|
||||
sh, err := sshx.New(o)
|
||||
if err != nil {
|
||||
return fail(fmt.Errorf("SSH: %w", err))
|
||||
}
|
||||
out, err := sh.Run(ctx, "pveversion", nil)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "host key mismatch") {
|
||||
_, fp, _ := sshx.Scan(ctx, o)
|
||||
res.SSHMismatch, res.SSHFingerprint = true, fp
|
||||
return fail(fmt.Errorf("SSH: le nœud %s présente la clé d'hôte %s au lieu de %s enregistrée", o.Host, fp, sshx.Fingerprint(key)))
|
||||
}
|
||||
return w
|
||||
return fail(fmt.Errorf("SSH: %w", err))
|
||||
}
|
||||
if !has("/sdn", "SDN.Allocate") && !has("/sdn/zones", "SDN.Allocate") {
|
||||
w = append(w, "SDN.Allocate absent sur /sdn : la synchro SDN échouera")
|
||||
if !strings.HasPrefix(strings.TrimSpace(string(out)), "pve-manager/") {
|
||||
return fail(fmt.Errorf("SSH: %s is not a PVE node (pveversion: %q)", o.Host, strings.TrimSpace(string(out))))
|
||||
}
|
||||
if dcOptions && !has("/", "Sys.Modify") {
|
||||
w = append(w, "Sys.Modify absent sur / : les options datacenter ne pourront pas être écrites")
|
||||
}
|
||||
if !has("/", "Sys.Audit") {
|
||||
w = append(w, "Sys.Audit absent sur / : options datacenter et versions illisibles")
|
||||
}
|
||||
return w
|
||||
res.SSH, res.OK = true, true
|
||||
return res
|
||||
}
|
||||
|
||||
// nodes converts the versions read by the PVE client.
|
||||
|
||||
Reference in new issue
Block a user