(refactor): allign settings retrieval with k8s configMap, helm chart update

This commit is contained in:
Maksym Sadovnychyy 2025-10-17 21:34:48 +02:00
parent 7c962a4924
commit b413f2bf3a
13 changed files with 163 additions and 105 deletions

View File

@ -50,11 +50,15 @@ helm lint $chartPath
Write-Output "Rendering Helm chart for validation..." Write-Output "Rendering Helm chart for validation..."
helm template $projectName $chartPath -n $namespace | Out-Null helm template $projectName $chartPath -n $namespace | Out-Null
# Generate a unique rollout value (current Unix timestamp)
$rollme = [int][double]::Parse((Get-Date -UFormat %s))
# Deploy Helm release # Deploy Helm release
Write-Output "Deploying Helm release '$projectName'..." Write-Output "Deploying Helm release '$projectName'..."
helm upgrade --install $projectName $chartPath -n $namespace ` helm upgrade --install $projectName $chartPath -n $namespace `
--set imagePullSecret.create=false ` --set imagePullSecret.create=false `
--set imagePullSecrets[0].name=cr-maksit-pull ` --set imagePullSecrets[0].name=cr-maksit-pull `
--set-string "rollme=$rollme"
# Check deployment status # Check deployment status
Write-Output "Waiting for deployment rollout..." Write-Output "Waiting for deployment rollout..."

View File

@ -3,82 +3,18 @@
namespace MaksIT.LetsEncryptServer { namespace MaksIT.LetsEncryptServer {
public class Agent { public class Agent {
public required string AgentHostname { get; set; }
private string? _agentHostname; public required int AgentPort { get; set; }
public string AgentHostname { public required string AgentKey { get; set; }
get { public required string ServiceToReload { get; set; }
var env = Environment.GetEnvironmentVariable("MAKS-IT_AGENT_HOSTNAME");
return env ?? _agentHostname ?? string.Empty;
}
set {
_agentHostname = value;
}
}
private int? _agentPort;
public int AgentPort {
get {
var env = Environment.GetEnvironmentVariable("MAKS-IT_AGENT_PORT");
return env != null ? int.Parse(env) : _agentPort ?? 0;
}
set {
_agentPort = value;
}
}
private string? _agentKey;
public string AgentKey {
get {
var env = Environment.GetEnvironmentVariable("MAKS-IT_AGENT_KEY");
return env ?? _agentKey ?? string.Empty;
}
set {
_agentKey = value;
}
}
private string? _serviceToReload;
public string ServiceToReload {
get {
var env = Environment.GetEnvironmentVariable("MAKS-IT_AGENT_SERVICE");
return env ?? _serviceToReload ?? string.Empty;
}
set {
_serviceToReload = value;
}
}
} }
public class Configuration : ILetsEncryptConfiguration { public class Configuration : ILetsEncryptConfiguration {
public required string Production { get; set; }
public required string Staging { get; set; }
private string? _production; public required string CacheFolder { get; set; }
public string Production { public required string AcmeFolder { get; set; }
get {
var env = Environment.GetEnvironmentVariable("LETSENCRYPT_SERVER_PRODUCTION");
return env ?? _production ?? string.Empty;
}
set {
_production = value;
}
}
private string? _staging;
public string Staging {
get {
var env = Environment.GetEnvironmentVariable("LETSENCRYPT_SERVER_STAGING");
return env ?? _staging ?? string.Empty;
}
set {
_staging = value;
}
}
public required Agent Agent { get; set; } public required Agent Agent { get; set; }
} }

View File

@ -10,20 +10,13 @@ namespace MaksIT.LetsEncryptServer.Controllers;
[Route(".well-known")] [Route(".well-known")]
public class WellKnownController : ControllerBase { public class WellKnownController : ControllerBase {
private readonly Configuration _appSettings;
private readonly ICertsRestChallengeService _certsFlowService; private readonly ICertsRestChallengeService _certsFlowService;
private readonly string _acmePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "acme");
public WellKnownController( public WellKnownController(
IOptions<Configuration> appSettings, IOptions<Configuration> appSettings,
ICertsFlowService certsFlowService ICertsFlowService certsFlowService
) { ) {
_appSettings = appSettings.Value;
_certsFlowService = certsFlowService; _certsFlowService = certsFlowService;
if (!Directory.Exists(_acmePath))
Directory.CreateDirectory(_acmePath);
} }

View File

@ -10,6 +10,16 @@ var builder = WebApplication.CreateBuilder(args);
// Extract configuration // Extract configuration
var configuration = builder.Configuration; var configuration = builder.Configuration;
var configMapPath = Path.Combine(Path.DirectorySeparatorChar.ToString(), "configMap", "appsettings.json");
if (File.Exists(configMapPath)) {
configuration.AddJsonFile(configMapPath, optional: false, reloadOnChange: true);
}
var secretsPath = Path.Combine(Path.DirectorySeparatorChar.ToString(), "secrets", "appsecrets.json");
if (File.Exists(secretsPath)) {
configuration.AddJsonFile(secretsPath, optional: false, reloadOnChange: true);
}
// Configure strongly typed settings objects // Configure strongly typed settings objects
var configurationSection = configuration.GetSection("Configuration"); var configurationSection = configuration.GetSection("Configuration");
var appSettings = configurationSection.Get<Configuration>() ?? throw new ArgumentNullException(); var appSettings = configurationSection.Get<Configuration>() ?? throw new ArgumentNullException();

View File

@ -4,6 +4,7 @@
using MaksIT.Core.Extensions; using MaksIT.Core.Extensions;
using MaksIT.LetsEncrypt.Entities; using MaksIT.LetsEncrypt.Entities;
using MaksIT.Results; using MaksIT.Results;
using Microsoft.Extensions.Options;
namespace MaksIT.LetsEncryptServer.Services; namespace MaksIT.LetsEncryptServer.Services;
@ -19,14 +20,13 @@ public class CacheService : ICacheService, IDisposable {
private readonly string _cacheDirectory; private readonly string _cacheDirectory;
private readonly LockManager _lockManager; private readonly LockManager _lockManager;
public CacheService(ILogger<CacheService> logger) { public CacheService(
ILogger<CacheService> logger,
IOptions<Configuration> appsettings
) {
_logger = logger; _logger = logger;
_cacheDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache"); _cacheDirectory = appsettings.Value.CacheFolder;
_lockManager = new LockManager(); _lockManager = new LockManager();
if (!Directory.Exists(_cacheDirectory)) {
Directory.CreateDirectory(_cacheDirectory);
}
} }
/// <summary> /// <summary>

View File

@ -64,9 +64,7 @@ public class CertsFlowService : ICertsFlowService {
_letsEncryptService = letsEncryptService; _letsEncryptService = letsEncryptService;
_cacheService = cashService; _cacheService = cashService;
_agentService = agentService; _agentService = agentService;
_acmePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "acme"); _acmePath = _appSettings.AcmeFolder;
if (!Directory.Exists(_acmePath))
Directory.CreateDirectory(_acmePath);
} }
#region Common methods #region Common methods

View File

@ -11,12 +11,15 @@
"Production": "https://acme-v02.api.letsencrypt.org/directory", "Production": "https://acme-v02.api.letsencrypt.org/directory",
"Staging": "https://acme-staging-v02.api.letsencrypt.org/directory", "Staging": "https://acme-staging-v02.api.letsencrypt.org/directory",
"Agent": { "CacheFolder": "/cache",
"AgentHostname": "http://websrv0001.corp.maks-it.com", "AcmeFolder": "/acme",
"AgentPort": 9000,
"AgentKey": "UGnCaElLLJClHgUeet/yr7vNvPf13b1WkDJQMfsiP6I=",
"ServiceToReload": "haproxy" "Agent": {
"AgentHostname": "",
"AgentPort": 9000,
"AgentKey": "",
"ServiceToReload": "haproxy"
} }
} }
} }

View File

@ -22,15 +22,11 @@ services:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_HTTP_PORTS=5000 - ASPNETCORE_HTTP_PORTS=5000
- LETSENCRYPT_SERVER_PRODUCTION=https://acme-v02.api.letsencrypt.org/directory
- LETSENCRYPT_SERVER_STAGING=https://acme-staging-v02.api.letsencrypt.org/directory
- MAKS_IT_AGENT_HOSTNAME=http://websrv0001.corp.maks-it.com
- MAKS-IT_AGENT_PORT=5000
- MAKS-IT_AGENT_KEY=UGnCaElLLJClHgUeet/yr7vNvPf13b1WkDJQMfsiP6I=
- MAKS-IT_AGENT_SERVICE=haproxy
volumes: volumes:
- D:\Compose\MaksIT.CertsUI\acme:/app/bin/Debug/net8.0/acme - D:\Compose\MaksIT.CertsUI\acme:/acme
- D:\Compose\MaksIT.CertsUI\cache:/app/bin/Debug/net8.0/cache - D:\Compose\MaksIT.CertsUI\cache:/cache
- D:\Compose\MaksIT.CertsUI\configMap\appsettings.json:/configMap/appsettings.json:ro
- D:\Compose\MaksIT.CertsUI\secrets\appsecrets.json:/secrets/appsecrets.json:ro
networks: networks:
- maks-it - maks-it

View File

@ -0,0 +1,39 @@
Thank you for installing **{{ .Chart.Name }}**!
This chart deploys the MaksIT CertsUI tool for automated Let's Encrypt HTTPS certificate renewal.
------------------------------------------------------------
## Components
- **Server**: Handles certificate requests and renewal logic.
- **Client**: Web UI for managing and viewing certificate status.
- **Reverse Proxy**: Exposes the UI and API endpoints.
------------------------------------------------------------
## Configuration
- **Secrets**:
The server uses a Kubernetes Secret (`appsecrets.json`) for sensitive data.
- **ConfigMap**:
The server uses a ConfigMap (`appsettings.json`) for application settings.
- **Persistence**:
PVCs are created for `/acme` and `/cache` directories.
------------------------------------------------------------
## Uninstall
To remove all resources created by this chart:
```
helm uninstall {{ .Release.Name }}
```
------------------------------------------------------------
## Notes
- Certificates are renewed automatically using Let's Encrypt.
- You can customize settings in `values.yaml` before installation.
- For advanced configuration, see the chart documentation and templates.
------------------------------------------------------------

View File

@ -3,13 +3,17 @@
{{- end }} {{- end }}
{{- define "certs-ui.fullname" -}} {{- define "certs-ui.fullname" -}}
{{- $name := .Chart.Name -}} {{- if .Values.fullnameOverride -}}
{{- $rel := .Release.Name -}} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- if or (hasPrefix (printf "%s-" $name) $rel) (eq $rel $name) -}} {{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- $rel := .Release.Name -}}
{{- if contains $name $rel -}}
{{- $rel | trunc 63 | trimSuffix "-" -}} {{- $rel | trunc 63 | trimSuffix "-" -}}
{{- else -}} {{- else -}}
{{- printf "%s-%s" $rel $name | trunc 63 | trimSuffix "-" -}} {{- printf "%s-%s" $rel $name | trunc 63 | trimSuffix "-" -}}
{{- end -}} {{- end -}}
{{- end -}}
{{- end }} {{- end }}
{{- define "certs-ui.chart" -}} {{- define "certs-ui.chart" -}}

View File

@ -0,0 +1,27 @@
{{- $root := . -}}
{{- range $compName, $comp := .Values.components }}
{{- if $comp.configMapFile }}
{{- $cf := $comp.configMapFile -}}
{{- $cmName := printf "%s-%s-configmap" (include "certs-ui.fullname" $root) $compName -}}
{{- $existing := lookup "v1" "ConfigMap" $root.Release.Namespace $cmName -}}
{{- if and $cf.keep $existing }}
{{/* keep=true and ConfigMap exists -> render nothing */}}
{{- else }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ $cmName }}
labels:
{{- include "certs-ui.labels" $root | nindent 4 }}
app.kubernetes.io/component: {{ $compName }}
{{- if $cf.keep }}
annotations:
"helm.sh/resource-policy": keep
{{- end }}
data:
{{ $cf.key }}: |
{{ $cf.content | indent 4 }}
{{- end }}
{{- end }}
{{- end }}

View File

@ -1,3 +1,5 @@
{{- $roll := ((.Values.rollme | default (now | unixEpoch)) | toString) -}}
{{- $root := . -}} {{- $root := . -}}
{{- range $compName, $comp := .Values.components }} {{- range $compName, $comp := .Values.components }}
--- ---
@ -22,7 +24,7 @@ spec:
app.kubernetes.io/component: {{ $compName }} app.kubernetes.io/component: {{ $compName }}
{{- if and $comp.secretsFile $comp.secretsFile.forceUpdate }} {{- if and $comp.secretsFile $comp.secretsFile.forceUpdate }}
annotations: annotations:
"checksum/secrets-file": {{ (default "" $comp.secretsFile.content) | toString | sha256sum | quote }} rollme: "{{$roll}}"
{{- end }} {{- end }}
spec: spec:
{{- include "certs-ui.imagePullSecrets" $root | nindent 6 }} {{- include "certs-ui.imagePullSecrets" $root | nindent 6 }}
@ -37,6 +39,8 @@ spec:
containerPort: {{ $tgt }} containerPort: {{ $tgt }}
{{- if $comp.env }} {{- if $comp.env }}
env: env:
- name: ROLLOUT_TOKEN
value: "{{$roll}}"
{{- range $comp.env }} {{- range $comp.env }}
- name: {{ .name }} - name: {{ .name }}
value: {{ .value | quote }} value: {{ .value | quote }}
@ -57,6 +61,14 @@ spec:
mountPath: {{ $comp.secretsFile.mountPath }} mountPath: {{ $comp.secretsFile.mountPath }}
subPath: {{ base $comp.secretsFile.mountPath }} subPath: {{ base $comp.secretsFile.mountPath }}
{{- end }} {{- end }}
{{- if $comp.configMapFile }}
- name: {{ $compName }}-configmap
configMap:
name: {{ include "certs-ui.fullname" $root }}-{{ $compName }}-configmap
items:
- key: {{ $comp.configMapFile.key }}
path: {{ base $comp.configMapFile.mountPath }}
{{- end }}
{{- end }} {{- end }}
{{- if or $hasVols $hasSecret }} {{- if or $hasVols $hasSecret }}
volumes: volumes:
@ -79,5 +91,13 @@ spec:
- key: {{ $comp.secretsFile.key }} - key: {{ $comp.secretsFile.key }}
path: {{ base $comp.secretsFile.mountPath }} path: {{ base $comp.secretsFile.mountPath }}
{{- end }} {{- end }}
{{- if $comp.configMapFile }}
- name: {{ $compName }}-configmap
configMap:
name: {{ include "certs-ui.fullname" $root }}-{{ $compName }}-configmap
items:
- key: {{ $comp.configMapFile.key }}
path: {{ base $comp.configMapFile.mountPath }}
{{- end }}
{{- end }} {{- end }}
{{- end }} {{- end }}

View File

@ -49,6 +49,34 @@ components:
keep: true keep: true
forceUpdate: false forceUpdate: false
configMapFile:
key: appsettings.json
mountPath: /configMap/appsettings.json
content: |
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Configuration": {
"Production": "https://acme-v02.api.letsencrypt.org/directory",
"Staging": "https://acme-staging-v02.api.letsencrypt.org/directory",
"CacheFolder": "/cache",
"AcmeFolder": "/acme",
"Agent": {
"AgentHostname": "http://websrv0001.corp.maks-it.com",
"AgentPort": 9000,
"ServiceToReload": "haproxy"
}
}
}
keep: true
forceUpdate: false
client: client:
replicas: 1 replicas: 1
image: image: