Отпечаток сервера для сервера лицензий: internal/hwid (machine-id, UUID платформы через /etc/bj/hw-id от установщика, MAC), передаётся при активации и продлении
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -26,6 +26,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/hwid"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"math/big"
|
"math/big"
|
||||||
@@ -385,7 +386,8 @@ func (in *installer) stepLicense() (string, error) {
|
|||||||
}
|
}
|
||||||
host, _ := os.Hostname()
|
host, _ := os.Hostname()
|
||||||
body, _ := json.Marshal(map[string]string{"code": strings.TrimSpace(in.code), "install_id": installID,
|
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)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, in.server+"/v1/activate", bytes.NewReader(body))
|
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) {
|
func (in *installer) stepServices() (string, error) {
|
||||||
|
_ = hwid.WriteCache(in.root) // хэш UUID платформы для bj (читается только root)
|
||||||
var files []string
|
var files []string
|
||||||
err := fs.WalkDir(deploy.Files, ".", func(p string, d fs.DirEntry, err error) error {
|
err := fs.WalkDir(deploy.Files, ".", func(p string, d fs.DirEntry, err error) error {
|
||||||
if err != nil || d.IsDir() {
|
if err != nil || d.IsDir() {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/hwid"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -78,6 +79,7 @@ type licenseReply struct {
|
|||||||
func licenseCall(ctx context.Context, path string, body map[string]string) (*licenseReply, error) {
|
func licenseCall(ctx context.Context, path string, body map[string]string) (*licenseReply, error) {
|
||||||
host, _ := os.Hostname()
|
host, _ := os.Hostname()
|
||||||
body["hostname"], body["version"], body["machine"] = host, BuildVersion, machineHash()
|
body["hostname"], body["version"], body["machine"] = host, BuildVersion, machineHash()
|
||||||
|
body["board"], body["mac"] = hwid.Board(), hwid.MAC() // отпечаток сервера: хэши, без самих значений
|
||||||
b, _ := json.Marshal(body)
|
b, _ := json.Marshal(body)
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, licenseServer+path, bytes.NewReader(b))
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, licenseServer+path, bytes.NewReader(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user