Первичная настройка, часть 4: HTTPS веб-интерфейса (:8443, самоподписанный сертификат на имя и адреса сервера, свой сертификат, перенаправление браузера с HTTP); сервер времени из настроек (ntpd/chrony/timesyncd — bj-system-admin); постоянный адрес — bj-network (netplan try с откатом), вопрос при первой загрузке с образа; раздел «Сервер: HTTPS, время, сеть»; HTTPS в итоговой проверке; адреса https:// на экране установщика и консоли
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package lkgateway
|
||||
|
||||
// Сервер времени (NTP) — из веб-интерфейса (05.10.2026). В закрытом контуре внешние серверы времени
|
||||
// недоступны и часы уплывают, а время важно для подписей и обмена с НРД. Меняет root-скрипт
|
||||
// bj-system-admin (ntpd, chrony или systemd-timesyncd — что стоит на сервере).
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TimeSettings — серверы времени, заданные в bj.
|
||||
type TimeSettings struct {
|
||||
Servers string `json:"servers,omitempty"`
|
||||
SavedAt time.Time `json:"saved_at,omitempty"`
|
||||
}
|
||||
|
||||
// timeStatus — синхронизируются ли часы и текущее время сервера.
|
||||
func timeStatus(ctx context.Context) (synced bool, now string) {
|
||||
cctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
out, err := exec.CommandContext(cctx, "timedatectl", "show", "-p", "NTPSynchronized", "--value").Output()
|
||||
return err == nil && strings.TrimSpace(string(out)) == "yes", time.Now().Format("02.01.2006 15:04:05 MST")
|
||||
}
|
||||
|
||||
func registerTime(mux *http.ServeMux, rc *RuntimeConfig) {
|
||||
mux.HandleFunc("/admin/setup/time", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
servers := strings.Join(strings.Fields(strings.NewReplacer(",", " ", ";", " ").Replace(r.FormValue("servers"))), " ")
|
||||
if servers == "" {
|
||||
setupFlash(w, r, "Время: укажите сервер времени (NTP) — адрес или имя")
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
|
||||
defer cancel()
|
||||
res, err := runKeymediaOp(ctx, "system-ntp", map[string]string{"extra": servers})
|
||||
if err != nil {
|
||||
setupFlash(w, r, "Время: "+err.Error())
|
||||
return
|
||||
}
|
||||
if res["ok"] != true {
|
||||
setupFlash(w, r, "Время: "+pickStr(asString(res["error"]), "не применено"))
|
||||
return
|
||||
}
|
||||
_ = rc.UpdateTime(TimeSettings{Servers: servers, SavedAt: time.Now()})
|
||||
setupFlash(w, r, "Время: "+asString(res["output"])+". Синхронизация займёт до нескольких минут.")
|
||||
})
|
||||
}
|
||||
|
||||
func asString(v any) string {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user