From d67324e943197c568600cd00eac7624b6dea5495 Mon Sep 17 00:00:00 2001 From: zuevav Date: Fri, 25 Sep 2026 00:19:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=82=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D0=BA=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=B0=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=20=D0=BB=D0=B8=D1=86=D0=B5=D0=BD=D0=B7=D0=B8=D0=B9:=20internal?= =?UTF-8?q?/hwid=20(machine-id,=20UUID=20=D0=BF=D0=BB=D0=B0=D1=82=D1=84?= =?UTF-8?q?=D0=BE=D1=80=D0=BC=D1=8B=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20/et?= =?UTF-8?q?c/bj/hw-id=20=D0=BE=D1=82=20=D1=83=D1=81=D1=82=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D1=89=D0=B8=D0=BA=D0=B0,=20MAC),=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B0=D1=91=D1=82=D1=81=D1=8F=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=B0=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B4=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi --- cmd/bj-installer/main.go | 5 +- internal/hwid/hwid.go | 78 +++++++++++++++++++++++++++++ internal/lkgateway/licenseclient.go | 2 + 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 internal/hwid/hwid.go diff --git a/cmd/bj-installer/main.go b/cmd/bj-installer/main.go index 67ea4c8..ef9d2e8 100644 --- a/cmd/bj-installer/main.go +++ b/cmd/bj-installer/main.go @@ -26,6 +26,7 @@ import ( "errors" "flag" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/hwid" "io" "io/fs" "math/big" @@ -385,7 +386,8 @@ func (in *installer) stepLicense() (string, error) { } host, _ := os.Hostname() body, _ := json.Marshal(map[string]string{"code": strings.TrimSpace(in.code), "install_id": installID, - "hostname": host, "version": "bj-installer " + installerVersion, "machine": machineHash()}) + "hostname": host, "version": "bj-installer " + installerVersion, "machine": machineHash(), + "board": hwid.Board(), "mac": hwid.MAC()}) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() req, _ := http.NewRequestWithContext(ctx, http.MethodPost, in.server+"/v1/activate", bytes.NewReader(body)) @@ -726,6 +728,7 @@ func (in *installer) stepValidata() (string, error) { } func (in *installer) stepServices() (string, error) { + _ = hwid.WriteCache(in.root) // хэш UUID платформы для bj (читается только root) var files []string err := fs.WalkDir(deploy.Files, ".", func(p string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() { diff --git a/internal/hwid/hwid.go b/internal/hwid/hwid.go new file mode 100644 index 0000000..7a79ffd --- /dev/null +++ b/internal/hwid/hwid.go @@ -0,0 +1,78 @@ +// Package hwid — отпечаток сервера для сервера лицензий: хэши (sha256) трёх признаков, сами +// значения никуда не передаются. По ним сервер лицензий узнаёт один и тот же сервер у разных +// организаций (защита от «отключил лицензию у одной компании — включил у другой»). +// - machine: /etc/machine-id — меняется при переустановке ОС; +// - board: UUID платформы (BIOS / гипервизор, /sys/class/dmi/id/product_uuid) — у виртуальной +// машины сохраняется при переустановке ОС; читается только root, поэтому установщик кладёт +// его хэш в /etc/bj/hw-id, а bj читает оттуда; +// - mac: MAC-адреса сетевых карт (физических и virtio), отсортированные. +package hwid + +import ( + "crypto/sha256" + "encoding/hex" + "os" + "path/filepath" + "sort" + "strings" +) + +// CacheFile — хэш UUID платформы, записанный установщиком (root) для bj. +const CacheFile = "/etc/bj/hw-id" + +func sum(s string) string { + s = strings.TrimSpace(s) + if s == "" { + return "" + } + h := sha256.Sum256([]byte(s)) + return hex.EncodeToString(h[:]) +} + +// Machine — хэш /etc/machine-id. +func Machine() string { + b, _ := os.ReadFile("/etc/machine-id") + return sum(string(b)) +} + +// Board — хэш UUID платформы: из DMI (под root) или из файла, записанного установщиком. +func Board() string { + if b, err := os.ReadFile("/sys/class/dmi/id/product_uuid"); err == nil { + if v := strings.ToLower(strings.TrimSpace(string(b))); v != "" && v != "00000000-0000-0000-0000-000000000000" { + return sum(v) + } + } + b, _ := os.ReadFile(CacheFile) + return strings.TrimSpace(string(b)) +} + +// MAC — хэш отсортированных MAC-адресов сетевых карт, у которых есть устройство (не lo, не мосты). +func MAC() string { + ents, _ := os.ReadDir("/sys/class/net") + var macs []string + for _, e := range ents { + dir := filepath.Join("/sys/class/net", e.Name()) + if _, err := os.Stat(filepath.Join(dir, "device")); err != nil { + continue + } + b, err := os.ReadFile(filepath.Join(dir, "address")) + if a := strings.ToLower(strings.TrimSpace(string(b))); err == nil && a != "" && a != "00:00:00:00:00:00" { + macs = append(macs, a) + } + } + sort.Strings(macs) + return sum(strings.Join(macs, ",")) +} + +// WriteCache — записать хэш UUID платформы для bj (вызывает установщик под root). +func WriteCache(root string) error { + b := Board() + if b == "" { + return nil + } + p := filepath.Join(root, CacheFile) + if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil { + return err + } + return os.WriteFile(p, []byte(b+"\n"), 0o644) +} diff --git a/internal/lkgateway/licenseclient.go b/internal/lkgateway/licenseclient.go index 90567eb..f64bb4c 100644 --- a/internal/lkgateway/licenseclient.go +++ b/internal/lkgateway/licenseclient.go @@ -13,6 +13,7 @@ import ( "encoding/json" "errors" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/hwid" "io" "log" "net/http" @@ -78,6 +79,7 @@ type licenseReply struct { func licenseCall(ctx context.Context, path string, body map[string]string) (*licenseReply, error) { host, _ := os.Hostname() body["hostname"], body["version"], body["machine"] = host, BuildVersion, machineHash() + body["board"], body["mac"] = hwid.Board(), hwid.MAC() // отпечаток сервера: хэши, без самих значений b, _ := json.Marshal(body) req, err := http.NewRequestWithContext(ctx, http.MethodPost, licenseServer+path, bytes.NewReader(b)) if err != nil {