diff --git a/.dockerignore b/.dockerignore index d3efc04..373ab56 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,6 +11,7 @@ **/*.user **/*.dbmdl **/*.jfm +**/*.db **/secrets.dev.yaml **/values.dev.yaml **/Tiltfile diff --git a/Dockerfile b/Dockerfile index b367782..90029eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,9 +21,21 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl4 \ && rm -rf /var/lib/apt/lists/* +# Run as non-root +RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser +RUN mkdir -p /app/Data && chown appuser:appgroup /app/Data + COPY --from=build /app/publish . +RUN chown -R appuser:appgroup /app + +USER appuser ENV ASPNETCORE_URLS=http://+:8080 +ENV ASPNETCORE_ENVIRONMENT=Production + +# SQLite database lives here — mount a persistent volume at /app/Data +VOLUME ["/app/Data"] + EXPOSE 8080 ENTRYPOINT ["dotnet", "EntKube.Web.dll"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..efcf487 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,59 @@ +services: + postgres: + image: postgres:17 + environment: + POSTGRES_DB: entkube + POSTGRES_USER: entkube + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-changeme}" + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U entkube -d entkube"] + interval: 5s + timeout: 5s + retries: 10 + start_period: 10s + restart: unless-stopped + + entkube: + build: + context: . + dockerfile: Dockerfile + image: entkube:latest + ports: + - "8080:8080" + volumes: + - entkube-data:/app/Data + depends_on: + postgres: + condition: service_healthy + environment: + # --- Database --- + DatabaseProvider: Postgres + ConnectionStrings__DefaultConnection: "Host=postgres;Port=5432;Database=entkube;Username=entkube;Password=${POSTGRES_PASSWORD:-changeme}" + + # --- Vault encryption --- + # Required. Generate with: openssl rand -base64 32 + Vault__RootKey: "REPLACE_WITH_BASE64_32_BYTE_KEY" + + # --- Auth --- + # Set to false after the first admin account is created. + Auth__AllowRegistration: "true" + + # --- Bootstrap --- + # Optional. If set, this user is granted the Admin role on every startup + # (no-op if already admin). Useful for recovering access. + # Seed__AdminEmail: "admin@example.com" + + # --- Email / SMTP (optional) --- + # Smtp__Host: "smtp.example.com" + # Smtp__Username: "user" + # Smtp__Password: "password" + # Smtp__FromAddress: "alerts@entkube.io" + restart: unless-stopped + +volumes: + # Named volumes are NOT removed by "docker compose down". + # Data is only deleted if you explicitly run "docker compose down -v". + postgres-data: + entkube-data: diff --git a/src/EntKube.Web/Components/Account/Pages/Register.razor b/src/EntKube.Web/Components/Account/Pages/Register.razor index f7612cf..db3a472 100644 --- a/src/EntKube.Web/Components/Account/Pages/Register.razor +++ b/src/EntKube.Web/Components/Account/Pages/Register.razor @@ -14,48 +14,58 @@ @inject ILogger Logger @inject NavigationManager NavigationManager @inject IdentityRedirectManager RedirectManager +@inject IConfiguration Configuration Register -

Register

+@if (!allowRegistration) +{ +

Registration Closed

+

Self-registration is currently disabled. Please contact an administrator to get access.

+} +else +{ +

Register

-
-
- - - -

Create a new account.

-
- -
- - - -
-
- - - -
-
- - - -
- -
+
+
+ + + +

Create a new account.

+
+ +
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+
+

Use another service to register.

+
+ +
+
-
-
-

Use another service to register.

-
- -
-
-
+} @code { private IEnumerable? identityErrors; + private bool allowRegistration = true; [SupplyParameterFromForm] private InputModel Input { get; set; } = default!; @@ -68,10 +78,17 @@ protected override void OnInitialized() { Input ??= new(); + allowRegistration = Configuration.GetValue("Auth:AllowRegistration", true); } public async Task RegisterUser(EditContext editContext) { + if (!allowRegistration) + { + RedirectManager.RedirectTo("Account/Register"); + return; + } + var user = CreateUser(); await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); diff --git a/src/EntKube.Web/Components/Layout/NavMenu.razor b/src/EntKube.Web/Components/Layout/NavMenu.razor index 3bea427..67d2823 100644 --- a/src/EntKube.Web/Components/Layout/NavMenu.razor +++ b/src/EntKube.Web/Components/Layout/NavMenu.razor @@ -3,6 +3,7 @@ @inject NavigationManager NavigationManager @inject AuthenticationStateProvider AuthStateProvider @inject EntKube.Web.Services.UserAccessService UserAccessService +@inject IConfiguration Configuration - @* Advanced YAML editor *@ -
-
-

- -

- @if (showAdvancedYaml) - { -
- -
- } -
-
- - @* Action bar *@ -
- - @if (comp.Status == ComponentStatus.NotInstalled || comp.Status == ComponentStatus.Failed) - { - - } - else if (comp.Status == ComponentStatus.Installed) - { - - } -
+ } + else if (compDetailTab == "values") + { + } else if (compDetailTab == "secrets") { @@ -1325,6 +1294,27 @@ else if (section == "components") }
} + + @if (compDetailTab is "config" or "values") + { +
+ + @if (comp.Status == ComponentStatus.NotInstalled || comp.Status == ComponentStatus.Failed) + { + + } + else if (comp.Status == ComponentStatus.Installed) + { + + } +
+ } } diff --git a/src/EntKube.Web/Components/Pages/Tenants/EnvironmentTab.razor b/src/EntKube.Web/Components/Pages/Tenants/EnvironmentTab.razor index 2e3bc5a..9258270 100644 --- a/src/EntKube.Web/Components/Pages/Tenants/EnvironmentTab.razor +++ b/src/EntKube.Web/Components/Pages/Tenants/EnvironmentTab.razor @@ -52,9 +52,7 @@ else if (selectedEnv is not null) {

Paste your kubeconfig YAML or upload a file to auto-detect clusters and API server URLs.

- +