Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9587d11e21 |
@@ -239,8 +239,7 @@ func runServer() {
|
||||
meta.Register(api, mods)
|
||||
meta.RegisterHealth(api, sessions)
|
||||
meta.RegisterWhoami(api, sessions, roles, mods)
|
||||
meta.ConfigPath = configPath
|
||||
meta.RegisterUpdate(api)
|
||||
meta.RegisterUpdate(api, configPath)
|
||||
|
||||
auth.RegisterLogin(api, sessions, auditStore, cfg.SecureCookie())
|
||||
auth.RegisterLogout(api, sessions, cfg.SecureCookie())
|
||||
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
@@ -24,7 +25,7 @@ func serverCert(certPath, keyPath string) (tls.Certificate, error) {
|
||||
}
|
||||
|
||||
func generateSelfSignedCert() (tls.Certificate, error) {
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
|
||||
@@ -110,13 +110,25 @@ func Load(path string) (*File, error) {
|
||||
return nil, fmt.Errorf("parse config %s: %w", path, err)
|
||||
}
|
||||
// release_repo, when set, is downloaded over the wire and (for /api/update)
|
||||
// executed. Reject http:// at the boundary so /install.sh and the updater
|
||||
// never have to re-check.
|
||||
// executed. Validate shape + scheme once here so /install.sh and the updater
|
||||
// can use the string directly. Trim any trailing slash so downstream string
|
||||
// concatenation produces a clean URL.
|
||||
if f.Server.ReleaseRepo != "" {
|
||||
f.Server.ReleaseRepo = strings.TrimRight(f.Server.ReleaseRepo, "/")
|
||||
u, err := url.Parse(f.Server.ReleaseRepo)
|
||||
if err != nil || u.Scheme != "https" {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("server.release_repo: %w", err)
|
||||
}
|
||||
if u.Scheme != "https" {
|
||||
return nil, fmt.Errorf("server.release_repo must use https:// (got %q)", f.Server.ReleaseRepo)
|
||||
}
|
||||
if u.Host == "" {
|
||||
return nil, fmt.Errorf("server.release_repo missing host: %q", f.Server.ReleaseRepo)
|
||||
}
|
||||
parts := strings.Split(strings.Trim(u.Path, "/"), "/")
|
||||
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
||||
return nil, fmt.Errorf("server.release_repo must be https://host/owner/repo, got %q", f.Server.ReleaseRepo)
|
||||
}
|
||||
}
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
@@ -12,19 +12,18 @@ import (
|
||||
"github.com/danielgtaylor/huma/v2"
|
||||
)
|
||||
|
||||
// ConfigPath is set at startup so the update handler can re-load config and
|
||||
// surface release_repo / parse errors to the caller instead of only stderr.
|
||||
var ConfigPath string
|
||||
|
||||
// RegisterUpdate wires POST /api/meta/update. It runs the equivalent of
|
||||
// RegisterUpdate wires POST /api/update. It runs the equivalent of
|
||||
// `sudo nadir update` in a detached session and returns 202 immediately; the
|
||||
// systemctl restart that ends the updater drops in-flight connections, so the
|
||||
// caller should poll /api/health to confirm the new version is up.
|
||||
//
|
||||
// configPath is re-read by the handler so a missing release_repo (or any other
|
||||
// config error) surfaces as 4xx/5xx to the caller, not as stderr only.
|
||||
//
|
||||
// Authorization: requires (meta, root). Only roles with a wildcard grant
|
||||
// (the default admin role) match, since "meta" isn't a real module with a
|
||||
// declared permission vocabulary.
|
||||
func RegisterUpdate(api huma.API) {
|
||||
func RegisterUpdate(api huma.API, configPath string) {
|
||||
huma.Register(api, huma.Operation{
|
||||
OperationID: "meta-update",
|
||||
Method: "POST",
|
||||
@@ -36,13 +35,13 @@ func RegisterUpdate(api huma.API) {
|
||||
Errors: []int{400, 401, 403, 500},
|
||||
DefaultStatus: 202,
|
||||
}, func(ctx context.Context, _ *struct{}) (*oscmd.StatusOutput, error) {
|
||||
if ConfigPath != "" {
|
||||
cfg, err := config.Load(ConfigPath)
|
||||
if configPath != "" {
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
return nil, huma.Error500InternalServerError("config load failed", err)
|
||||
}
|
||||
if cfg.Server.ReleaseRepo == "" {
|
||||
return nil, huma.Error400BadRequest("server.release_repo not set in " + ConfigPath)
|
||||
return nil, huma.Error400BadRequest("server.release_repo not set in " + configPath)
|
||||
}
|
||||
}
|
||||
exe, err := os.Executable()
|
||||
|
||||
@@ -160,10 +160,11 @@ func registerLocale(api huma.API) {
|
||||
Metadata: op("read"),
|
||||
Errors: readErrors,
|
||||
}, func(ctx context.Context, _ *struct{}) (*KeymapsOutput, error) {
|
||||
keymaps, err := oscmd.RunLines("localectl", "list-keymaps")
|
||||
if err != nil {
|
||||
return nil, huma.Error500InternalServerError("localectl failed", err)
|
||||
}
|
||||
// ponytail: minimal servers ship without kbd / /usr/share/keymaps, so
|
||||
// localectl errors instead of returning empty. Treat that as "no keymaps
|
||||
// available" rather than a server fault — set-keymap stays unreachable
|
||||
// because nothing will be in the allowlist.
|
||||
keymaps, _ := oscmd.RunLines("localectl", "list-keymaps")
|
||||
out := &KeymapsOutput{}
|
||||
out.Body.Keymaps = keymaps
|
||||
return out, nil
|
||||
@@ -185,10 +186,9 @@ func registerLocale(api huma.API) {
|
||||
if km == "" {
|
||||
return nil, huma.Error400BadRequest("empty keymap")
|
||||
}
|
||||
keymaps, err := oscmd.RunLines("localectl", "list-keymaps")
|
||||
if err != nil {
|
||||
return nil, huma.Error500InternalServerError("localectl failed", err)
|
||||
}
|
||||
// list-keymaps failure means no keymap allowlist on this host (kbd absent);
|
||||
// fall through to unknown-keymap 400 instead of 500.
|
||||
keymaps, _ := oscmd.RunLines("localectl", "list-keymaps")
|
||||
if !slices.Contains(keymaps, km) {
|
||||
return nil, huma.Error400BadRequest("unknown keymap: " + km)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user