diff --git a/Dockerfile.console b/Dockerfile.console index 9b16ce5..b818c7d 100644 --- a/Dockerfile.console +++ b/Dockerfile.console @@ -19,20 +19,11 @@ COPY protogen/ protogen/ RUN CGO_ENABLED=0 GOOS=linux GOFLAGS="-ldflags=-s -ldflags=-w" go build -o console ./cmd/console/ # Runtime stage -FROM debian:bookworm-slim - -ENV TZ=Europe/Zurich - -# Add ca-certificates and basic tools needed for interactive console -RUN apt-get update && apt-get install --no-install-recommends -y \ - ca-certificates \ - jq \ - readline-common \ - && rm -rf /var/lib/apt/lists/* +FROM alpine:latest # Create user and group -RUN groupadd dop && \ - useradd -r --uid 1001 -g dop dop +RUN addgroup -g 1001 dop && \ + adduser -D -u 1001 -G dop dop # Set working directory RUN mkdir -p /app @@ -44,8 +35,8 @@ COPY .env.${MINEXUS_ENV} ./.env.${MINEXUS_ENV} # Copy the binary from builder COPY --from=builder /app/console /app/console -# Create entrypoint script -RUN echo '#!/bin/sh\n/app/console' > /app/docker-entrypoint.sh && \ +# Create entrypoint script (use printf for proper newlines) +RUN printf '#!/bin/sh\nexec /app/console\n' > /app/docker-entrypoint.sh && \ chmod +x /app/docker-entrypoint.sh # Set ownership for Kubernetes compatibility diff --git a/Dockerfile.minion b/Dockerfile.minion index 3ce6020..019cae4 100644 --- a/Dockerfile.minion +++ b/Dockerfile.minion @@ -19,19 +19,11 @@ COPY protogen/ protogen/ RUN CGO_ENABLED=0 GOOS=linux GOFLAGS="-ldflags=-s -ldflags=-w" go build -o minion ./cmd/minion/ # Runtime stage -FROM debian:bookworm-slim - -ENV TZ=Europe/Zurich - -# Add ca-certificates and basic tools -RUN apt-get update && apt-get install --no-install-recommends -y \ - ca-certificates \ - jq \ - && rm -rf /var/lib/apt/lists/* +FROM alpine:latest # Create user and group -RUN groupadd dop && \ - useradd -r --uid 1001 -g dop dop +RUN addgroup -g 1001 dop && \ + adduser -D -u 1001 -G dop dop # Set working directory RUN mkdir -p /app @@ -43,8 +35,8 @@ COPY .env.${MINEXUS_ENV} ./.env.${MINEXUS_ENV} # Copy the binary from builder COPY --from=builder /app/minion /app/minion -# Create entrypoint script -RUN echo '#!/bin/sh\n/app/minion' > /app/docker-entrypoint.sh && \ +# Create entrypoint script (use printf for proper newlines) +RUN printf '#!/bin/sh\nexec /app/minion\n' > /app/docker-entrypoint.sh && \ chmod +x /app/docker-entrypoint.sh # Set ownership for Kubernetes compatibility diff --git a/Dockerfile.nexus b/Dockerfile.nexus index 73f1860..77308f1 100644 --- a/Dockerfile.nexus +++ b/Dockerfile.nexus @@ -19,20 +19,11 @@ COPY protogen/ protogen/ RUN CGO_ENABLED=0 GOOS=linux GOFLAGS="-ldflags=-s -ldflags=-w" go build -o nexus ./cmd/nexus/ # Runtime stage -FROM debian:bookworm-slim +FROM alpine:latest -ENV TZ=Europe/Zurich - -# Add ca-certificates and basic tools -RUN apt-get update && apt-get install --no-install-recommends -y \ - netcat-traditional \ - ca-certificates \ - jq \ - && rm -rf /var/lib/apt/lists/* - -# Create user and group -RUN groupadd dop && \ - useradd -r --uid 1001 -g dop dop +# Create user and group (use -G for group assignment in Alpine) +RUN addgroup -g 1001 dop && \ + adduser -D -u 1001 -G dop dop # Set working directory RUN mkdir -p /app @@ -45,10 +36,10 @@ COPY .env.${MINEXUS_ENV} ./.env.${MINEXUS_ENV} COPY --from=builder /app/nexus /app/nexus # Copy webroot directory for web server assets -COPY webroot/ /app/webroot/ +COPY internal/web/webroot/ /app/webroot/ -# Create entrypoint script -RUN echo '#!/bin/sh\n/app/nexus' > /app/docker-entrypoint.sh && \ +# Create entrypoint script (use printf for proper newlines) +RUN printf '#!/bin/sh\nexec /app/nexus\n' > /app/docker-entrypoint.sh && \ chmod +x /app/docker-entrypoint.sh # Set ownership for Kubernetes compatibility diff --git a/Makefile b/Makefile index 9f504cc..00dbeb2 100644 --- a/Makefile +++ b/Makefile @@ -99,26 +99,29 @@ build-test: ## compose-build: Build Docker images for specified environment (default: test) compose-build: @export MINEXUS_ENV=$${MINEXUS_ENV:-test}; \ - set -a; . ./.env.$$MINEXUS_ENV; set +a; \ echo "Building Docker images for $$MINEXUS_ENV environment..."; \ [ "$$MINEXUS_ENV" = "prod" ] && $(MAKE) certs-prod || true; \ - docker compose build + docker compose --env-file .env.$$MINEXUS_ENV build ## compose-run: Run application in specified environment (default: test) compose-run: @export MINEXUS_ENV=$${MINEXUS_ENV:-test}; \ - set -a; . ./.env.$$MINEXUS_ENV; set +a; \ echo "Starting application in $$MINEXUS_ENV mode..."; \ $(MAKE) compose-stop; \ $(MAKE) compose-build; \ - docker compose up -d + docker compose --env-file .env.$$MINEXUS_ENV up -d ## compose-stop: Stop services for specified environment (default: test) compose-stop: @export MINEXUS_ENV=$${MINEXUS_ENV:-test}; \ - set -a; . ./.env.$$MINEXUS_ENV; set +a; \ echo "Stopping $$MINEXUS_ENV environment..."; \ - docker compose down --remove-orphans + docker compose --env-file .env.$$MINEXUS_ENV down --remove-orphans + +## compose-up: Start services without rebuilding for specified environment (default: test) +compose-up: + @export MINEXUS_ENV=$${MINEXUS_ENV:-test}; \ + echo "Starting $$MINEXUS_ENV environment without rebuild..."; \ + docker compose --env-file .env.$$MINEXUS_ENV up ## certs-clean: remove copied certificates from root certs directory certs-clean: @@ -258,9 +261,8 @@ local: compose-run ## logs-docker: Follow logs for specified environment (default: test) logs-docker: @export MINEXUS_ENV=$${MINEXUS_ENV:-test}; \ - set -a; . ./.env.$$MINEXUS_ENV; set +a; \ echo "Following logs for $$MINEXUS_ENV environment..."; \ - docker compose logs -f + docker compose --env-file .env.$$MINEXUS_ENV logs -f ## minion: build minion client (production environment) minion: diff --git a/README.md b/README.md index 36ee952..87ebaa6 100644 --- a/README.md +++ b/README.md @@ -66,17 +66,46 @@ So I decided to make this agent (minion) the server (nexus) and start by impleme ## Quick Start -create an .env.prod file -```cp env.sample .env.prod``` +### 1. Setup Nexus Server + +Create an .env.prod file: +```bash +cp env.sample .env.prod +``` Modify the .env.prod (DON'T KEEP the default password unchanged...) -Then launch the nexus -```MINEXUS_ENV=prod make compose-run``` +Then launch the nexus: +```bash +MINEXUS_ENV=prod make compose-run +``` + +### 2. Install Minion Clients + +Once Nexus is running, visit the web dashboard at `http://yournexus.address.com:8086` to see quick installation commands with copy buttons. + +Alternatively, use these commands directly: + +**Linux/macOS:** +```bash +curl -sSL http://yournexus.address.com:8086/install_minion.sh | sh +``` + +**Windows PowerShell:** +```powershell +iwr -useb http://yournexus.address.com:8086/download/minion/windows-amd64.exe -OutFile minion.exe; .\minion.exe +``` + +**Options:** +- For systemd installation on Linux: `curl -sSL http://yournexus.address.com:8086/install_minion.sh | sh -s -- --systemd` +- For Windows with custom environment variables: + ```powershell + $env:NEXUS_SERVER="yournexus.address.com" + $env:NEXUS_MINION_PORT="11972" + .\minion.exe + ``` -From now on the hosts where you want to install minion, just -```curl http://yournexus.address.com:8086/install_minion.sh | sh``` -This will download and run the right minion for your OS/ARCH +The installation script automatically detects your OS and architecture, downloading the appropriate minion binary. ## Project Structure diff --git a/docker-compose.yml b/docker-compose.yml index 2be6b59..e5c2513 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,13 +24,13 @@ services: - default # Nexus server - depends on database being healthy - nexus_server: + nexus: build: context: . dockerfile: Dockerfile.nexus args: MINEXUS_ENV: ${MINEXUS_ENV:-test} - container_name: nexus_server + container_name: nexus env_file: - .env.${MINEXUS_ENV:-test} restart: always @@ -55,8 +55,8 @@ services: networks: - default healthcheck: - #test: ["CMD-SHELL", "nc -z nexus_server ${NEXUS_MINION_PORT:-11972} && nc -z nexus_server ${NEXUS_CONSOLE_PORT:-11973} && nc -z nexus_server ${NEXUS_WEB_PORT:-8086} && echo 'All three ports accessible' || exit 1"] - test: ["CMD-SHELL", "nc -z nexus_server ${NEXUS_MINION_PORT:-11972} && nc -z nexus_server ${NEXUS_CONSOLE_PORT:-11973} && echo 'All three ports accessible' || exit 1"] + #test: ["CMD-SHELL", "nc -z nexus ${NEXUS_MINION_PORT:-11972} && nc -z nexus ${NEXUS_CONSOLE_PORT:-11973} && nc -z nexus ${NEXUS_WEB_PORT:-8086} && echo 'All three ports accessible' || exit 1"] + test: ["CMD-SHELL", "nc -z nexus ${NEXUS_MINION_PORT:-11972} && nc -z nexus ${NEXUS_CONSOLE_PORT:-11973} && echo 'All three ports accessible' || exit 1"] interval: 3s timeout: 5s retries: 10 @@ -77,14 +77,14 @@ services: - MINEXUS_ENV=${MINEXUS_ENV:-test} - DEBUG=true - MINION_ID=${MINION_ID:-docker-minion} - - NEXUS_SERVER=nexus_server + - NEXUS_SERVER=nexus - NEXUS_MINION_PORT=${NEXUS_MINION_PORT:-11972} - HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-60} - INITIAL_RECONNECT_DELAY=${INITIAL_RECONNECT_DELAY:-1} - MAX_RECONNECT_DELAY=${MAX_RECONNECT_DELAY:-3600} - CONNECT_TIMEOUT=${CONNECT_TIMEOUT:-3} depends_on: - nexus_server: + nexus: condition: service_healthy networks: - default @@ -102,11 +102,11 @@ services: environment: - MINEXUS_ENV=${MINEXUS_ENV:-test} - DEBUG=${DEBUG:-false} - - NEXUS_SERVER=nexus_server + - NEXUS_SERVER=nexus - NEXUS_CONSOLE_PORT=${NEXUS_CONSOLE_PORT:-11973} - CONNECT_TIMEOUT=${CONNECT_TIMEOUT:-3} depends_on: - nexus_server: + nexus: condition: service_healthy networks: - default diff --git a/integration_test.go b/integration_test.go index 7cc1a8a..c68bc6b 100644 --- a/integration_test.go +++ b/integration_test.go @@ -123,7 +123,7 @@ const ( // // Required Services: // - nexus_db: PostgreSQL database -// - nexus_server: Nexus gRPC dual-port server (port 11972 for minions, 11973 for console) +// - nexus: Nexus gRPC dual-port server (port 11972 for minions, 11973 for console) // - minion_1: Test minion client // // Test Categories: @@ -385,7 +385,7 @@ func setupDockerServices(t *testing.T) { parseDuration := time.Since(parseStart) t.Logf("TIMING: Docker status parsing took %v", parseDuration) - requiredServices := []string{"nexus_db", "nexus_server", "minion"} + requiredServices := []string{"nexus_db", "nexus", "minion"} missingServices := []string{} for _, service := range requiredServices { @@ -399,7 +399,7 @@ func setupDockerServices(t *testing.T) { // Start services serviceStartStart := time.Now() - cmd = exec.Command("docker", "compose", "up", "-d", "nexus_server", "minion") + cmd = exec.Command("docker", "compose", "up", "-d", "nexus", "minion") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -432,9 +432,9 @@ func parseDockerComposePS(output string) map[string]string { services["nexus_db"] = "running" } } - if strings.Contains(line, "nexus_server") { + if strings.Contains(line, "nexus") { if strings.Contains(line, "running") { - services["nexus_server"] = "running" + services["nexus"] = "running" } } if strings.Contains(line, "minion") { @@ -2128,7 +2128,7 @@ func waitForCommandCompletion(t *testing.T, commandID string, maxAttempts int, s // executeNexusRestart executes the nexus server restart command func executeNexusRestart() error { - restartCmd := exec.Command("docker", "compose", "restart", "nexus_server") + restartCmd := exec.Command("docker", "compose", "restart", "nexus") restartCmd.Stdout = os.Stdout restartCmd.Stderr = os.Stderr return restartCmd.Run() diff --git a/internal/certs/certs.go b/internal/certs/certs.go index 3996058..6a7fdb8 100644 --- a/internal/certs/certs.go +++ b/internal/certs/certs.go @@ -4,6 +4,10 @@ import _ "embed" // Static certificate files embedded at build time // These certificates provide a consistent PKI across all builds and deployments +// +// Note: The files in internal/certs/files/ are placeholder test certificates +// that prevent build errors. They are automatically replaced with proper +// certificates by the Makefile during build/test processes. var ( // Certificate Authority diff --git a/internal/certs/files/ca.crt b/internal/certs/files/ca.crt new file mode 100644 index 0000000..8087db1 --- /dev/null +++ b/internal/certs/files/ca.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFQzCCAyugAwIBAgIUfDOkqVEWwK4zsOFYy1mcSyklk1IwDQYJKoZIhvcNAQEL +BQAwMTEYMBYGA1UEAwwPTWluZXh1cy10ZXN0IENBMRUwEwYDVQQKDAxNaW5leHVz +LXRlc3QwHhcNMjUwNjIyMTEwOTMyWhcNMzUwNjIwMTEwOTMyWjAxMRgwFgYDVQQD +DA9NaW5leHVzLXRlc3QgQ0ExFTATBgNVBAoMDE1pbmV4dXMtdGVzdDCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMD2+RNwkj66jtxKU3HAXvyNBhoKnVWw +lnJPTV7AqbGRok/9uDJqYsQ9zLbGvNRTMgle5yuHqV6v19iHDaU7uUXgY8vCoTIc +DZG9n1khIAnMSRXzSacn0Wbv0B8jeEx+Dz81vHpJlw8XV9Qqu1gFTKz9Csv/cefE +heAEfD7AS5gIYHycrAdz/s1QwJrqwHL8tGZb+rCfLcCMMj20FPjbX8djJFSNtdSu +UFAiiAXAygiKlgcbP9+T0mO0l6Gpyv16b5G+8VGQYVUMQg/BGaklJ2HfqQ4yCw2L +4s0EdQ0DF3YKJ1AQUi1iNl7SdbjoYdKqkgyC3PvsIiV98JOxV+OV1Utxjo1bYl5U +8yaQ/n46T1BMtqKojnvgvLOjIaAQ/XD1jcEgBcVP9fPbi/onM76rx74f3T+6Gm+E +PtEz4VN2WnIqFQNp+f/4Nd5g97q7SmLhMNjfCOWO6O4Jj1/BL9R3+NgIXnwV+10T +fxsHSK5N2pFMEEE0Oo7aYlRlpSbIaEOG/DE3z9OJbuh1oX5u2zQB/39vrqwacZKA +yZp77J00s/2kPJdfACQY13mDNzppSIdMzZwfJotEZm2NAEfE5c/Y8E3kgOBq/HnM +Zqc9V7Y3eV1BLWxRvQyJW604cMC43yq1dRXEuR3h9kolwO89FfxW3bcrm/oON8a0 +LsabTaAyHefTAgMBAAGjUzBRMB0GA1UdDgQWBBSzKR/t3uHznVxtKUjGwjjkpz0C +STAfBgNVHSMEGDAWgBSzKR/t3uHznVxtKUjGwjjkpz0CSTAPBgNVHRMBAf8EBTAD +AQH/MA0GCSqGSIb3DQEBCwUAA4ICAQAfdc28NYw/Wm6O//pijFUNE5H/DUBOhOw/ +tcHyNB3xlAX5genhdUSRU9NvGm01h+KZqCRpZybFaaLMMgJJ7NLttScsuCib5v2t +DYFSzBI4OUYZM2JPiYSM15dybKWlmUh1WVtWhG3NmgZhsilQMzmh419qeiCP93F+ +7sCPk3yqelP5gw3F4LHt57SDtXyxNvmLKpsvNiIDF2E8y+Y3WZx3ua31KCKNl2z6 +6/tpKZQEHzv6eSvWnq/BQxi/PKw83Hd6bt71wKbT9HdTDwwVWVc6bJFiKUFmuu7e +CmePatNt6BEk94cV6BOAvUZCFueXgc6Jglxwc9Z4AVDAOEE9v809d5Kfdu46dz2I +C+Q6Dcjdsr9BAhETfZDWZkTSBw4849H9RNSNNHfMyJqPsByvKhNgdYBqNgMq3xaJ +wasxQQ+TEx/mzOmSX9Z4dzVUbnGE9bnvVvItVm3MgpCTkWGdWccIcDQqbP7ZQznA +EjZ/4oXjzVsNbxgEEzJDlr+db473rRE1/yihFOEUgbAv+HU+RTkKRf+gq+OMUxjQ +goAIPV+eL4O64jDI/jBNY81J4mJdz04O+gVpZZ/5Q5LT2Nxca5mdK3PYHx1lKJQH +/OODakzvT4baCNJv5kOwf4j1ijrM2Okum9H1IbMRgrD5BV4Y/oo43wUXKzBJpiZi +O9VC828vaA== +-----END CERTIFICATE----- diff --git a/internal/certs/files/ca.key b/internal/certs/files/ca.key new file mode 100644 index 0000000..d891ba9 --- /dev/null +++ b/internal/certs/files/ca.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +2X5Q65ylDFI0PI7QDqzblo1Ik6fX +-----END PRIVATE KEY----- diff --git a/internal/certs/files/console.crt b/internal/certs/files/console.crt new file mode 100644 index 0000000..a37f7a4 --- /dev/null +++ b/internal/certs/files/console.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIUAuxMBA3qX5+01uYDJzBI3Kr/+0EwDQYJKoZIhvcNAQEL +BQAwMTEYMBYGA1UEAwwPTWluZXh1cy10ZXN0IENBMRUwEwYDVQQKDAxNaW5leHVz +LXRlc3QwHhcNMjUwNjI5MTAzNzEzWhcNMzUwNjI3MTAzNzEzWjAkMRAwDgYDVQQD +DAdjb25zb2xlMRAwDgYDVQQKDAdNaW5leHVzMIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAsjfm5iceN69V2QSL+SPK6ofiCoILhUOlZD/QKfJsd2z9dJ6c +aykbktwdw12RNIcHOlVWtCO5qq2f2vc/r70mPc9IiQdeMgM/nKywmnDqSoGwt4pt +jNmOWnxGfzF1F/5F/mNq1wIsvwT9bivaH0WfOjKEOwMtIUO+9EVncRq4a4d2NLyA +8c8AQnzh0TFzinMKqGJ0cySyOm6i4H51CJEhNtxuftd25CT1RIfTYiK4D1RCDcDI +SAgbwqYR8IvzEJfdXjgqkGOUERRz34Li79WzAG7nz7/uMGT9z4fl9DZxdCptqq6H +pQ0HLB93FwVVtmRJDTjInc1NiYRWKEYHl5mM/VxQOE/A57nqzMIrjrKRKARzKA3A +kSOdjUpkev+1Iqq/mWQHybWjxSLSA+Vq0aSclwUgTVjOx60ngMA8zRZaMUpm2D+H +j8I7xMvFtrv7QI4ddzYWXQ0/CxjpLLP3cZ0WD6O7cViu1rKUIChJhyT2AZH0OF9C +yzMRO29ojzpM4EohAYdHSLYo9p+XUChO0CjMSSTWmMUPX8rF2NB6AnCubxWOK1xq +d58dhQ96LOjU3bh0r5Jhxra/TlsmKFb+0GqflMj8+FLP6hxtFcMapLxD4qAT/dtD +NpdAtqZJJvRaRAzltoh0dnSUETabzVvBH7ATA3YwMQD/8IlszYE7VktkdccCAwEA +AaNkMGIwCwYDVR0PBAQDAgSwMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB0GA1UdDgQW +BBS5MVFUXh7BvGLOBF1t1Rhn0+CItzAfBgNVHSMEGDAWgBSzKR/t3uHznVxtKUjG +wjjkpz0CSTANBgkqhkiG9w0BAQsFAAOCAgEAJokAajK+q6n712MH5nnXEDqkYbi+ +MGb2sdjOOZTsw2VFCC4pqfEnAi8EDmmMPd/906ivSEvqu8pS5nb/7CzWdNkyariK ++fUnUVcX91rAXBCfCuc2zTYKQKMIQbHiOWZBrjXZeV7DSpi9mCyHcZQrG6Cx5Jua ++JU47Ltike0kBOVjlcdFRqfdwGsI1Qc3I48aTv9OaZMc7T00BIgyLab9SwhMMLw6 +mvSi8Sw/QRZxsumGSBXocpXxWEkSj181t2t86/6sLg9SKKSLCkYpSjL+gxrPyyS2 +5fDZ1+liqeeJfW1WEV0QJY7D60USz+AIVfWFXU+T0x/eEP1JZfVRUuPg7bYtb/9S +hRNj5csLodk20pa/8aN2DrvTa61Z7/zDSd+CK3glMlgH1OibUb8xYigU4YlalZuK +JfRKeZN+wKy3HC5tY/zl+et0lffjDmI4Q2hu+Z/jOU3d1OHEkJXKE3E+jbA4c1ww +295ed4s7VudYmYttMwQZPWJQPAI7ShiXdIsaCXd2qpud+3UbEnwKT8PMhl708wy/ +Q5EH6jOGzl+43RvVodDlKuAGzrNcQbbEqXm48ftzukvc/e52Z2HDUuJqKj2hyBah +T0s8yHlXNL3wIVpT4NcjefsFDoRonQT4GP3PD2gnoDYD1Gxj/0s+U/yNm9KfGBJ5 +D0HIusaYliouet8= +-----END CERTIFICATE----- diff --git a/internal/certs/files/console.key b/internal/certs/files/console.key new file mode 100644 index 0000000..e3740a9 --- /dev/null +++ b/internal/certs/files/console.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +ZIPKmVjHAtc1hwe8pXRUE5CWg7rt1g== +-----END PRIVATE KEY----- diff --git a/internal/certs/files/server.crt b/internal/certs/files/server.crt new file mode 100644 index 0000000..486c9b3 --- /dev/null +++ b/internal/certs/files/server.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFjzCCA3egAwIBAgIURm45CeK+CFDK2yR9QZSkRw4l9TowDQYJKoZIhvcNAQEL +BQAwMTEYMBYGA1UEAwwPTWluZXh1cy10ZXN0IENBMRUwEwYDVQQKDAxNaW5leHVz +LXRlc3QwHhcNMjUwNjI5MTU1MzMzWhcNMzUwNjI3MTU1MzMzWjAnMQ4wDAYDVQQD +DAVuZXh1czEVMBMGA1UECgwMTWluZXh1cy10ZXN0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAry54ZgAwTLnV1QNXxQin9gtvdS6IkLmVeYPzYd5e3gQX +mH1uxpeNMAQIGq1fuU73NWupCBYnrWp2PJcL/aIb2k8DjZbT6c5m8DEHD+4wQQkL +Z6r9nvE6breAWZtkAs++zyoWJHt5JNDblYUORlpe9aicgO+cFQI/1UqTQfCqdhEj +lfHswPQN5xXNBqJx3Vbmx0tnYlszFlOXE/quiWTrxer3vcGncbj8uCCgZm86fWKe +UhxguwW0Y3WQ/4MNnDU/CaSQkTp2YGqoGue8/DnXgeK9gEwLQxGtsRzv1OadzsM9 +JpMiiCXd0okUwDdQ5oxbDm6jVy8nkstJnrWl6FA7sEMyltE4VhBvXO1yDpwQuSNU +2F5qt0UCqZDJcmk+7NFhVQEXGDSweFN0iljnvJRaf13Zi2Gc3W/lRobfB1zL6VGx +GOlhAl/YB2Kk3Fh8wLP0Rtfi5yRddCAqWKkmn+SMlBwIIa19BAIDsstQd/loit1w +ghRPzZQM5wBiVI1hCSPN11/fQ6DQz0TjhqGo8/GFcX0aCRRW/+Y/do2nTnLSJ7BG +DmxGg+o5AXcoQCd0IAt5j8EtjJJ4H9R6kZNpAnzccf7l2GI1yEwC76KLwwji1X94 +Q60fFisBuaZjscMR7tOTnGTGg7pNU1Z1ifAfjWQOh4ENfD7vtKtS8nNg7DMy7XcC +AwEAAaOBqDCBpTALBgNVHQ8EBAMCBLAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwQQYD +VR0RBDowOIIFbmV4dXOCDG5leHVzX3NlcnZlcoIJbG9jYWxob3N0hwR/AAABhxAA +AAAAAAAAAAAAAAAAAAABMB0GA1UdDgQWBBSwCoDni2OVlLkbCTCrdK4xD2YhkTAf +BgNVHSMEGDAWgBSzKR/t3uHznVxtKUjGwjjkpz0CSTANBgkqhkiG9w0BAQsFAAOC +AgEAb5UzvPJ9skhuwB/6usObpOX3wG44DhFMaCwbbhLkK4S860Ddb3nr+a4ohYcr +Z9yeyfDM/PcVggMJftuorH5kwDBILM3BIN6m973Ywrn3ADbWERzvk3fBToJ3WsOV +ETtFNaiuE25BSEi6JJAMbpUiEo9mFCWfcm3yiFL1QtJhOpAzFoYebs5tTlbytslh +PetVjItZn7FsyEzfjMDo49na6tYlxTB/56fmB137mh2hLtUTMdIDl97TGM6c1/T8 +ZkpFNdaVatS5XaQE9ajFMvqqRJsNxVSDdw5fFsykPaZvtK1DXyFvv4CHr/sTGDO+ +CEvT85BJsd2puHFU5Op9Y8NXG+QTjUNY0L6Bi4aeDDnLRs6ppm6cL2bssJDjrWA3 +7FxNNf8+4tSBRL7OpHSqGAez9oG01APexY7D+j4aMEYwwKzJlfbpterB5iosjZ3O +bp05nud7KvnG93B8hwIBdBWUUho248sNeT3k4Gw9ClIB+0V4A5C7ndD3LuRen09A +hKWdq3ebCHDfYRoZYCoCvEPmW36WLN8PhOByN1iQ6/OJnZi+jW+4KDIzyGFVV7Qo +nrB2PFR9Rztek6QvhoHJASlaPpZxVanfZTc+7SKJ0BKWfOjHbrxl0KzxiRUiQ3eO +I80PW9kj2RR84aBOG2K8JP7jnWRSNky/GBeN0sGpvh/MYzI= +-----END CERTIFICATE----- diff --git a/internal/certs/files/server.key b/internal/certs/files/server.key new file mode 100644 index 0000000..023fffe --- /dev/null +++ b/internal/certs/files/server.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +qq07dCFQYAWTYeSYHXDOgumMLAi/rg== +-----END PRIVATE KEY----- diff --git a/internal/config/config.go b/internal/config/config.go index d52d5ab..83c3c34 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,3 +1,6 @@ +// Package config provides configuration loading and validation for the Minexus application +// It supports environment-specific configurations, command line flags, and strict validation +// to ensure correct application behavior across different environments. package config import ( @@ -60,90 +63,28 @@ func (e ValidationError) Error() string { return fmt.Sprintf("configuration validation failed for %s=%s: %s", e.Field, e.Value, e.Message) } -// ConfigLoader provides unified configuration loading with priority handling -type ConfigLoader struct { +// Loader provides unified configuration loading with priority handling +type Loader struct { envVars map[string]string logger *zap.Logger } // NewConfigLoader creates a new configuration loader -func NewConfigLoader() *ConfigLoader { - return &ConfigLoader{ +func NewConfigLoader() *Loader { + return &Loader{ envVars: make(map[string]string), } } // WithLogger sets the logger for the config loader -func (cl *ConfigLoader) WithLogger(logger *zap.Logger) *ConfigLoader { +func (cl *Loader) WithLogger(logger *zap.Logger) *Loader { cl.logger = logger return cl } -// LoadEnvFile loads environment variables from .env file (LEGACY - use LoadEnvironmentFile) -func (cl *ConfigLoader) LoadEnvFile(filename string) error { - file, err := os.Open(filename) - if err != nil { - // File doesn't exist, not an error - if cl.logger != nil { - cl.logger.Debug("Environment file not found", zap.String("file", filename)) - } - return nil - } - defer file.Close() - - scanner := bufio.NewScanner(file) - lineNum := 0 - for scanner.Scan() { - lineNum++ - line := strings.TrimSpace(scanner.Text()) - - // Skip empty lines and comments - if line == "" || strings.HasPrefix(line, "#") { - continue - } - - // Parse KEY=VALUE format - parts := strings.SplitN(line, "=", 2) - if len(parts) != 2 { - if cl.logger != nil { - cl.logger.Warn("Invalid line in env file", - zap.String("file", filename), - zap.Int("line", lineNum), - zap.String("content", line)) - } - continue - } - - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - - // Remove quotes if present - if len(value) >= 2 { - if (strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`)) || - (strings.HasPrefix(value, `'`) && strings.HasSuffix(value, `'`)) { - value = value[1 : len(value)-1] - } - } - - cl.envVars[key] = value - } - - if err := scanner.Err(); err != nil { - return fmt.Errorf("error reading env file %s: %w", filename, err) - } - - if cl.logger != nil { - cl.logger.Debug("Loaded environment file", - zap.String("file", filename), - zap.Int("variables", len(cl.envVars))) - } - - return nil -} - // LoadEnvironmentFile loads environment variables from environment-specific file // Uses strict validation with no fallback - panics if environment file is missing -func (cl *ConfigLoader) LoadEnvironmentFile() error { +func (cl *Loader) LoadEnvironmentFile() error { env := DetectEnvironment() // Panics on invalid environment filename := GetEnvironmentFileName() @@ -208,7 +149,7 @@ func (cl *ConfigLoader) LoadEnvironmentFile() error { } // GetString gets string value with priority: flags → env → file → default -func (cl *ConfigLoader) GetString(key, defaultValue string) string { +func (cl *Loader) GetString(key, defaultValue string) string { // Check environment variables first (highest priority after flags) if value := os.Getenv(key); value != "" { return value @@ -224,7 +165,7 @@ func (cl *ConfigLoader) GetString(key, defaultValue string) string { } // GetInt gets int value with validation -func (cl *ConfigLoader) GetInt(key string, defaultValue int) (int, error) { +func (cl *Loader) GetInt(key string, defaultValue int) (int, error) { value := cl.GetString(key, "") if value == "" { return defaultValue, nil @@ -243,17 +184,17 @@ func (cl *ConfigLoader) GetInt(key string, defaultValue int) (int, error) { } // GetIntInRange gets int value with range validation -func (cl *ConfigLoader) GetIntInRange(key string, defaultValue, min, max int) (int, error) { +func (cl *Loader) GetIntInRange(key string, defaultValue, minValue, maxValue int) (int, error) { value, err := cl.GetInt(key, defaultValue) if err != nil { return 0, err } - if value < min || value > max { + if value < minValue || value > maxValue { return 0, ValidationError{ Field: key, Value: strconv.Itoa(value), - Message: fmt.Sprintf("must be between %d and %d", min, max), + Message: fmt.Sprintf("must be between %d and %d", minValue, maxValue), } } @@ -261,7 +202,7 @@ func (cl *ConfigLoader) GetIntInRange(key string, defaultValue, min, max int) (i } // GetBool gets bool value with validation -func (cl *ConfigLoader) GetBool(key string, defaultValue bool) (bool, error) { +func (cl *Loader) GetBool(key string, defaultValue bool) (bool, error) { value := cl.GetString(key, "") if value == "" { return defaultValue, nil @@ -280,7 +221,7 @@ func (cl *ConfigLoader) GetBool(key string, defaultValue bool) (bool, error) { } // GetDuration gets duration value with validation -func (cl *ConfigLoader) GetDuration(key string, defaultValue time.Duration) (time.Duration, error) { +func (cl *Loader) GetDuration(key string, defaultValue time.Duration) (time.Duration, error) { value := cl.GetString(key, "") if value == "" { return defaultValue, nil @@ -304,7 +245,7 @@ func (cl *ConfigLoader) GetDuration(key string, defaultValue time.Duration) (tim } // ValidateNetworkAddress validates a network address -func (cl *ConfigLoader) ValidateNetworkAddress(key, value string) error { +func (cl *Loader) ValidateNetworkAddress(key, value string) error { if value == "" { return ValidationError{ Field: key, @@ -344,7 +285,7 @@ func (cl *ConfigLoader) ValidateNetworkAddress(key, value string) error { } // ValidateHostname validates a hostname (without port) -func (cl *ConfigLoader) ValidateHostname(key, value string) error { +func (cl *Loader) ValidateHostname(key, value string) error { if value == "" { return ValidationError{ Field: key, @@ -366,7 +307,7 @@ func (cl *ConfigLoader) ValidateHostname(key, value string) error { } // ValidateRequired ensures a required field is not empty -func (cl *ConfigLoader) ValidateRequired(key, value string) error { +func (cl *Loader) ValidateRequired(key, value string) error { if value == "" { return ValidationError{ Field: key, @@ -378,7 +319,7 @@ func (cl *ConfigLoader) ValidateRequired(key, value string) error { } // ValidateDirectory ensures a directory path is valid -func (cl *ConfigLoader) ValidateDirectory(key, value string) error { +func (cl *Loader) ValidateDirectory(key, value string) error { if value == "" { return ValidationError{ Field: key, @@ -782,7 +723,7 @@ func LoadMinionConfig() (*MinionConfig, error) { } // loadMinionEnvConfig loads configuration from environment variables -func loadMinionEnvConfig(loader *ConfigLoader, config *MinionConfig, validationErrors *[]error) { +func loadMinionEnvConfig(loader *Loader, config *MinionConfig, validationErrors *[]error) { // Load and validate server hostname nexusServer := loader.GetString("NEXUS_SERVER", "localhost") if err := loader.ValidateHostname("NEXUS_SERVER", nexusServer); err != nil { @@ -813,7 +754,7 @@ func loadMinionEnvConfig(loader *ConfigLoader, config *MinionConfig, validationE } // loadMinionTimeouts loads timeout-related configuration from environment variables -func loadMinionTimeouts(loader *ConfigLoader, config *MinionConfig, validationErrors *[]error) { +func loadMinionTimeouts(loader *Loader, config *MinionConfig, validationErrors *[]error) { timeoutConfigs := []struct { envVar string target *int @@ -865,7 +806,7 @@ func parseMinionFlags(config *MinionConfig) *minionFlagValues { } // applyMinionFlags applies command line flag values to the configuration -func applyMinionFlags(loader *ConfigLoader, config *MinionConfig, flags *minionFlagValues, validationErrors *[]error) { +func applyMinionFlags(loader *Loader, config *MinionConfig, flags *minionFlagValues, validationErrors *[]error) { // Apply and validate server address if err := loader.ValidateNetworkAddress("server", *flags.serverAddr); err != nil { *validationErrors = append(*validationErrors, err) diff --git a/internal/web/assets.go b/internal/web/assets.go index 08b0760..17a4662 100644 --- a/internal/web/assets.go +++ b/internal/web/assets.go @@ -9,22 +9,22 @@ import ( // Embedded webroot assets - included at build time var ( // HTML Templates - //go:embed templates/*.html + //go:embed webroot/templates/*.html templatesFS embed.FS // Static assets (CSS, JS, images) - //go:embed static/* + //go:embed webroot/static/* staticFS embed.FS ) // GetTemplates loads and parses embedded HTML templates func GetTemplates() (*template.Template, error) { - return template.ParseFS(templatesFS, "templates/*.html") + return template.ParseFS(templatesFS, "webroot/templates/*.html") } // GetStaticFS returns the embedded static file system func GetStaticFS() fs.FS { - staticSubFS, err := fs.Sub(staticFS, "static") + staticSubFS, err := fs.Sub(staticFS, "webroot/static") if err != nil { panic("failed to create static subdirectory: " + err.Error()) } diff --git a/internal/web/data.go b/internal/web/data.go index 051f2cd..d63412a 100644 --- a/internal/web/data.go +++ b/internal/web/data.go @@ -14,6 +14,7 @@ type DashboardData struct { MinionPort int `json:"minion_port"` ConsolePort int `json:"console_port"` WebPort int `json:"web_port"` + ServerHost string `json:"server_host"` Minions []MinionInfo `json:"minions"` } diff --git a/internal/web/handlers.go b/internal/web/handlers.go index 6b092e3..9401461 100644 --- a/internal/web/handlers.go +++ b/internal/web/handlers.go @@ -247,6 +247,12 @@ func (ws *WebServer) buildDashboardData() DashboardData { systemStatus = "warning" } + // Get server host from environment variable or use default + serverHost := os.Getenv("NEXUS_SERVER") + if serverHost == "" { + serverHost = "localhost" + } + return DashboardData{ Title: "Dashboard", Version: version.Component("Nexus"), @@ -256,6 +262,7 @@ func (ws *WebServer) buildDashboardData() DashboardData { MinionPort: ws.config.MinionPort, ConsolePort: ws.config.ConsolePort, WebPort: ws.config.WebPort, + ServerHost: serverHost, Minions: minions, } } diff --git a/internal/web/templates/dashboard.html b/internal/web/templates/dashboard.html deleted file mode 100644 index 5ea5cad..0000000 --- a/internal/web/templates/dashboard.html +++ /dev/null @@ -1,56 +0,0 @@ -{{define "content"}} -
Version: {{.Version}}
-Uptime: {{.Uptime}}
-Version: {{.Version}}
+Uptime: {{.Uptime}}
+Linux/macOS:
+curl -sSL http://{{.ServerHost}}:{{.WebPort}}/install_minion.sh | sh
+
+ Windows PowerShell:
+iwr -useb http://{{.ServerHost}}:{{.WebPort}}/download/minion/windows-amd64.exe -OutFile minion.exe; .\minion.exe
+
+
+ Options:
+ • Linux systemd: add | sh -s -- --systemd
+ • Windows with env vars: $env:NEXUS_SERVER="{{.ServerHost}}"; $env:NEXUS_MINION_PORT="{{.MinionPort}}"; .\minion.exe
+
Version: {{.Version}}
-Uptime: {{.Uptime}}
-