Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cad6c1f421 | |||
| 22e6812d4b |
+136
-8
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -8,8 +9,11 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
|
||||
"nadir/internal/auth"
|
||||
"nadir/internal/config"
|
||||
)
|
||||
@@ -137,6 +141,125 @@ func installService(args []string) error {
|
||||
isUnsecure := *unsecureOpt || optCount == 0
|
||||
isTrustProxy := *trustProxyOpt
|
||||
|
||||
cfgPath, err := resolveConfigPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
shouldWriteConfig := false
|
||||
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
|
||||
shouldWriteConfig = true
|
||||
}
|
||||
|
||||
username := getUsername()
|
||||
var logFiles map[string][]string
|
||||
|
||||
if fs.NFlag() == 0 && (isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())) {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
if !shouldWriteConfig {
|
||||
fmt.Printf("Configuration file already exists at %s. Overwrite? [y/N] (default n): ", cfgPath)
|
||||
overwriteInput, _ := reader.ReadString('\n')
|
||||
overwriteInput = strings.ToLower(strings.TrimSpace(overwriteInput))
|
||||
if overwriteInput != "y" && overwriteInput != "yes" {
|
||||
fmt.Println("Keeping existing configuration. Proceeding with installation...")
|
||||
if existingCfg, loadErr := config.Load(cfgPath); loadErr == nil {
|
||||
*hostnameOpt = existingCfg.Server.Hostname
|
||||
if p, err := strconv.Atoi(existingCfg.Server.Port); err == nil {
|
||||
*portOpt = p
|
||||
}
|
||||
isTLS = existingCfg.Server.TLSCert != "" && existingCfg.Server.TLSKey != ""
|
||||
isTrustProxy = existingCfg.Server.TrustProxy
|
||||
isUnsecure = !isTLS && !isTrustProxy
|
||||
}
|
||||
goto skipConfigPrompt
|
||||
}
|
||||
shouldWriteConfig = true
|
||||
}
|
||||
|
||||
fmt.Println("Configuring Nadir installation:")
|
||||
fmt.Println(" 1) Serve plaintext HTTP directly (unsecure) [default]")
|
||||
fmt.Println(" 2) Generate persistent self-signed TLS cert/key and enable HTTPS (tls)")
|
||||
fmt.Println(" 3) Serve plaintext HTTP behind a trusted TLS-terminating reverse proxy (trust-proxy)")
|
||||
fmt.Print("Enter choice [1-3] (default 1): ")
|
||||
choice, _ := reader.ReadString('\n')
|
||||
choice = strings.TrimSpace(choice)
|
||||
if choice == "" || choice == "1" {
|
||||
isUnsecure = true
|
||||
isTLS = false
|
||||
isTrustProxy = false
|
||||
} else if choice == "2" {
|
||||
isTLS = true
|
||||
isUnsecure = false
|
||||
isTrustProxy = false
|
||||
} else if choice == "3" {
|
||||
isTrustProxy = true
|
||||
isTLS = false
|
||||
isUnsecure = false
|
||||
} else {
|
||||
return fmt.Errorf("invalid choice: %q", choice)
|
||||
}
|
||||
|
||||
fmt.Printf("Enter hostname to bind to (default %s): ", *hostnameOpt)
|
||||
hostChoice, _ := reader.ReadString('\n')
|
||||
hostChoice = strings.TrimSpace(hostChoice)
|
||||
if hostChoice != "" {
|
||||
*hostnameOpt = hostChoice
|
||||
}
|
||||
|
||||
fmt.Printf("Enter port to bind to (default %d): ", *portOpt)
|
||||
portChoice, _ := reader.ReadString('\n')
|
||||
portChoice = strings.TrimSpace(portChoice)
|
||||
if portChoice != "" {
|
||||
p, err := strconv.Atoi(portChoice)
|
||||
if err != nil || p <= 0 || p > 65535 {
|
||||
return fmt.Errorf("invalid port: %q", portChoice)
|
||||
}
|
||||
*portOpt = p
|
||||
}
|
||||
|
||||
fmt.Printf("Enter main admin username (default %s): ", username)
|
||||
userChoice, _ := reader.ReadString('\n')
|
||||
userChoice = strings.TrimSpace(userChoice)
|
||||
if userChoice != "" {
|
||||
username = userChoice
|
||||
}
|
||||
|
||||
fmt.Print("Would you like to expose any log files to the Nadir UI? [y/N] (default n): ")
|
||||
logInput, _ := reader.ReadString('\n')
|
||||
logInput = strings.ToLower(strings.TrimSpace(logInput))
|
||||
if logInput == "y" || logInput == "yes" {
|
||||
logFiles = make(map[string][]string)
|
||||
for {
|
||||
fmt.Print(" Enter service/unit name (e.g. nginx): ")
|
||||
unit, _ := reader.ReadString('\n')
|
||||
unit = strings.TrimSpace(unit)
|
||||
if unit == "" {
|
||||
fmt.Println(" Service name cannot be empty. Skipping.")
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf(" Enter absolute path to log file for %s: ", unit)
|
||||
path, _ := reader.ReadString('\n')
|
||||
path = strings.TrimSpace(path)
|
||||
if path == "" {
|
||||
fmt.Println(" Path cannot be empty. Skipping.")
|
||||
continue
|
||||
}
|
||||
|
||||
logFiles[unit] = append(logFiles[unit], path)
|
||||
|
||||
fmt.Print(" Add another log file? [y/N] (default n): ")
|
||||
another, _ := reader.ReadString('\n')
|
||||
another = strings.ToLower(strings.TrimSpace(another))
|
||||
if another != "y" && another != "yes" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skipConfigPrompt:
|
||||
// Provision the PAM service the server authenticates against, so it exists
|
||||
// before the unit starts rather than appearing on first login. Idempotent:
|
||||
// EnsurePAMService leaves an existing /etc/pam.d/nadir untouched. runServer
|
||||
@@ -181,11 +304,6 @@ func installService(args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
cfgPath, err := resolveConfigPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Construct configuration template content based on installation options
|
||||
secureTLSVal := "true"
|
||||
trustProxyLine := "# trust_proxy: false"
|
||||
@@ -204,11 +322,21 @@ func installService(args []string) error {
|
||||
keyLine = "# tls_key: /var/lib/nadir/tls/key.pem"
|
||||
}
|
||||
|
||||
username := getUsername()
|
||||
configContent := fmt.Sprintf(configTemplateBase, secureTLSVal, trustProxyLine, certLine, keyLine, *hostnameOpt, *portOpt, username)
|
||||
if len(logFiles) > 0 {
|
||||
var logFilesSection strings.Builder
|
||||
logFilesSection.WriteString("\nlog_files:\n")
|
||||
for unit, paths := range logFiles {
|
||||
logFilesSection.WriteString(fmt.Sprintf(" %s:\n", unit))
|
||||
for _, path := range paths {
|
||||
logFilesSection.WriteString(fmt.Sprintf(" - %s\n", path))
|
||||
}
|
||||
}
|
||||
configContent += logFilesSection.String()
|
||||
}
|
||||
|
||||
// Ensure default config file exists
|
||||
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
|
||||
// Ensure default config file exists or we explicitly overwrote it
|
||||
if shouldWriteConfig {
|
||||
if err := saveDefaultConfig(cfgPath, configContent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jedisct1/go-minisign v0.0.0-20260527172527-a09352b57a22
|
||||
github.com/mattn/go-isatty v0.0.21 // indirect
|
||||
github.com/mattn/go-isatty v0.0.21
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
|
||||
+5
-1
@@ -99,7 +99,11 @@ do_install() {
|
||||
|
||||
echo "binary installed at /usr/local/bin/nadir"
|
||||
echo "installing as a systemd service ..."
|
||||
/usr/local/bin/nadir install
|
||||
if [ -c /dev/tty ]; then
|
||||
/usr/local/bin/nadir install < /dev/tty
|
||||
else
|
||||
/usr/local/bin/nadir install
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "done. check status with: nadir status"
|
||||
|
||||
Reference in New Issue
Block a user