локальный вход задаётся конфигом: заголовки подставляет сам сервис
- в конфиг добавлены секция [auth.test_headers] и предохранитель [server] debug: заголовки входа подставляет слой транспорта, второго процесса локальный запуск больше не требует - подкоманда devtools proxy удалена целиком: всё, ради чего её поднимали, делает сам сервис - адресного предохранителя нет по решению владельца — цена названа в ADR и в модели угроз
This commit is contained in:
+5
-82
@@ -7,13 +7,11 @@
|
||||
// тихо кладёт инструмент разработчика в боевой образ. Один пакет платит эти
|
||||
// четыре места **однажды**, сколько бы подкоманд в нём ни завелось.
|
||||
//
|
||||
// Подкоманд две: `proxy` — подставной обратный прокси, `resume` — возврат
|
||||
// остановленной записи в работу. Вторая встала на место панели владельца:
|
||||
// панели у сервиса больше нет, а экраны правки записи приносят отдельные задачи.
|
||||
//
|
||||
// Имена заголовков берутся **константами транспорта**, а не литералами: они
|
||||
// нормативны, и второй список разошёлся бы с первым молча — локальный вход
|
||||
// перестал бы узнавать кого бы то ни было, а искали бы поломку в сервисе.
|
||||
// Подкоманда одна — `resume`, возврат остановленной записи в работу. Она встала
|
||||
// на место панели владельца: панели у сервиса больше нет, а экраны правки
|
||||
// записи приносят отдельные задачи. Подставной обратный прокси жил здесь второй
|
||||
// подкомандой и убран 2026-08-23 задачей `config-test-headers-login`: заголовки
|
||||
// входа локального прогона подставляет сам сервис по своим настройкам.
|
||||
//
|
||||
// Вывод идёт stdlib-логом в поток ошибок, а не `slog`: его читает человек в
|
||||
// терминале, в сбор он не едет. Изъятие названо строкой в конвенции журнала.
|
||||
@@ -22,15 +20,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
httpcontroller "git.vakhrushev.me/av/transcriber/internal/controller/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -42,8 +34,6 @@ func main() {
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "proxy":
|
||||
runProxy(os.Args[2:])
|
||||
case "resume":
|
||||
runResume(os.Args[2:])
|
||||
default:
|
||||
@@ -57,74 +47,7 @@ func usage() {
|
||||
fmt.Fprint(os.Stderr, `Оснастка разработчика.
|
||||
|
||||
Подкоманды:
|
||||
proxy подставной обратный прокси: ставит заголовок и шлёт запрос сервису
|
||||
resume вернуть остановленную запись в работу
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
// runProxy поднимает подставной обратный прокси.
|
||||
//
|
||||
// Зачем он нужен: сервис узнаёт пришедшего по заголовку, который на сервере
|
||||
// ставит Caddy, сходив к Authelia. На машине разработчика ни того ни другого
|
||||
// нет, а браузер заголовков не ставит — значит приложение локально не
|
||||
// открывалось бы вовсе. Прокси встаёт на их место: слушает свой порт, ставит
|
||||
// заголовок и переправляет запрос сервису.
|
||||
//
|
||||
// Проверок он не делает никаких — ни пароля, ни группы, ни срока. Это его
|
||||
// назначение, а не упущение: пускать он должен всякого, кто до него дошёл, а
|
||||
// сам он слушает петлевой адрес.
|
||||
func runProxy(args []string) {
|
||||
flags := flag.NewFlagSet("proxy", flag.ExitOnError)
|
||||
listen := flags.String("listen", "127.0.0.1:9000", "адрес, на котором слушать")
|
||||
target := flags.String("target", "http://127.0.0.1:8080", "адрес сервиса")
|
||||
login := flags.String("user", "dev", "логин, которым называть пришедшего")
|
||||
name := flags.String("name", "Разработчик", "имя, пригодное к показу")
|
||||
email := flags.String("email", "", "адрес почты; пустой не ставится вовсе")
|
||||
if err := flags.Parse(args); err != nil {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// Слушаем петлевой адрес по умолчанию, и это часть назначения: прокси
|
||||
// пускает всякого, кто до него дошёл, и в чужой сети становится открытым
|
||||
// входом в сервис.
|
||||
upstream, err := url.Parse(*target)
|
||||
if err != nil {
|
||||
log.Fatalf("адрес сервиса не читается: %v", err)
|
||||
}
|
||||
|
||||
proxy := &httputil.ReverseProxy{
|
||||
Rewrite: func(r *httputil.ProxyRequest) {
|
||||
r.SetURL(upstream)
|
||||
|
||||
// Именно Set, а не Add. Прокси, который **добавляет** заголовок к
|
||||
// присланному, оставляет рядом со своим значением чужое — и сервис
|
||||
// отвергает такой запрос целиком, потому что двух значений он не
|
||||
// разбирает. Настоящий Caddy обязан делать то же самое, и это
|
||||
// записано требованием к контуру в модели угроз.
|
||||
r.Out.Header.Set(httpcontroller.LoginHeader, *login)
|
||||
r.Out.Header.Set(httpcontroller.NameHeader, *name)
|
||||
if *email != "" {
|
||||
r.Out.Header.Set(httpcontroller.EmailHeader, *email)
|
||||
} else {
|
||||
r.Out.Header.Del(httpcontroller.EmailHeader)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
log.Printf("подставной прокси: %s → %s, пришедший — %q", *listen, *target, *login)
|
||||
log.Printf("приложение открывать по http://%s", *listen)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: *listen,
|
||||
Handler: proxy,
|
||||
// Таймаута чтения нет намеренно: сервис принимает записи на несколько
|
||||
// часов, и прокси, обрывающий такую загрузку, ловил бы разработчика на
|
||||
// поломке, которой в сервисе нет.
|
||||
ReadTimeout: 0,
|
||||
}
|
||||
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
log.Fatalf("подставной прокси остановлен: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user