HTTPS: http:// перебрасывается на https://; имя сервера — с проверкой DNS; скрипты DEPO при обновлении
Порт HTTPS принимает и http://: 308 на https:// того же имени и порта (было «Client sent an HTTP request to an HTTPS server»). Доменное имя сервера сохраняется, только если оно есть в DNS и указывает на этот сервер; иначе — что добавить/исправить (запись A → IP сервера). Сообщение об успехе — с адресом https://имя:порт/ и честно про самоподписанный сертификат. Обновление bj с новыми процедурами DEPO: отпечаток установленного набора (depo_procs_applied) не совпадает со встроенным — на главной и в настройках плашка «требуются повышенные права на базу данных» с вводом логина/пароля администратора SQL (не сохраняются) и «Применить скрипты». Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
@@ -7,6 +7,7 @@ package lkgateway
|
||||
// ходят по HTTP на localhost и не перенаправляются.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
@@ -245,6 +246,7 @@ func startHTTPS(rc *RuntimeConfig, handler http.Handler) *http.Server {
|
||||
return nil
|
||||
}
|
||||
httpsUp.Store(true)
|
||||
ln = newHTTPSSniffListener(ln, rc.Snapshot().HTTPS.port())
|
||||
go func() {
|
||||
log.Printf("lk-gateway: listen https %s", srv.Addr)
|
||||
if err := srv.ServeTLS(ln, "", ""); err != nil && err != http.ErrServerClosed {
|
||||
@@ -326,3 +328,87 @@ func registerHTTPS(mux *http.ServeMux, rc *RuntimeConfig) {
|
||||
setupFlash(w, r, "HTTPS: создан новый самоподписанный сертификат на имя сервера и его адреса")
|
||||
})
|
||||
}
|
||||
|
||||
// httpsSniffListener — порт HTTPS принимает и обычный http://: такой запрос перенаправляется (308) на
|
||||
// https:// того же имени и порта, вместо ответа Go «Client sent an HTTP request to an HTTPS server».
|
||||
// TLS-соединение узнаётся по первому байту (0x16 — handshake) и идёт дальше без изменений.
|
||||
type httpsSniffListener struct {
|
||||
net.Listener
|
||||
conns chan net.Conn
|
||||
errs chan error
|
||||
port int
|
||||
}
|
||||
|
||||
func newHTTPSSniffListener(ln net.Listener, port int) net.Listener {
|
||||
l := &httpsSniffListener{Listener: ln, conns: make(chan net.Conn, 16), errs: make(chan error, 1), port: port}
|
||||
go func() {
|
||||
for {
|
||||
c, err := ln.Accept()
|
||||
if err != nil {
|
||||
l.errs <- err
|
||||
return
|
||||
}
|
||||
go l.sniff(c)
|
||||
}
|
||||
}()
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *httpsSniffListener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case c := <-l.conns:
|
||||
return c, nil
|
||||
case err := <-l.errs:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (l *httpsSniffListener) sniff(c net.Conn) {
|
||||
_ = c.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||
br := bufio.NewReader(c)
|
||||
b, err := br.Peek(1)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return
|
||||
}
|
||||
_ = c.SetReadDeadline(time.Time{})
|
||||
if b[0] == 0x16 {
|
||||
l.conns <- &peekedConn{Conn: c, r: br}
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
_ = c.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||
req, err := http.ReadRequest(br)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
host := req.Host
|
||||
if h, _, err := net.SplitHostPort(host); err == nil {
|
||||
host = h
|
||||
}
|
||||
if net.ParseIP(host) == nil && !validHostname(strings.ToLower(host)) {
|
||||
host = "localhost"
|
||||
}
|
||||
target := "https://" + net.JoinHostPort(host, strconv.Itoa(l.port)) + req.URL.RequestURI()
|
||||
_, _ = c.Write([]byte("HTTP/1.1 308 Permanent Redirect\r\nLocation: " + target + "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"))
|
||||
}
|
||||
|
||||
// peekedConn — соединение, у которого уже прочитан (подсмотрен) первый байт.
|
||||
type peekedConn struct {
|
||||
net.Conn
|
||||
r *bufio.Reader
|
||||
}
|
||||
|
||||
func (p *peekedConn) Read(b []byte) (int, error) { return p.r.Read(b) }
|
||||
|
||||
// serverIPs — адреса этого сервера (для проверки, куда указывает доменное имя).
|
||||
func serverIPs() []string {
|
||||
var out []string
|
||||
addrs, _ := net.InterfaceAddrs()
|
||||
for _, a := range addrs {
|
||||
if n, ok := a.(*net.IPNet); ok && !n.IP.IsLoopback() && !n.IP.IsLinkLocalUnicast() {
|
||||
out = append(out, n.IP.String())
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user