(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..."
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
Write-Output "Deploying Helm release '$projectName'..."
helm upgrade --install $projectName $chartPath -n $namespace `
--set imagePullSecret.create=false `
--set imagePullSecrets[0].name=cr-maksit-pull `
--set-string "rollme=$rollme"
# Check deployment status
Write-Output "Waiting for deployment rollout..."

View File

@ -3,82 +3,18 @@
namespace MaksIT.LetsEncryptServer {
public class Agent {
private string? _agentHostname;
public string AgentHostname {
get {
var env = Environment.GetEnvironmentVariable("MAKS-IT_AGENT_HOSTNAME");
return env ?? _agentHostname ?? string.Empty;
public required string AgentHostname { get; set; }
public required int AgentPort { get; set; }
public required string AgentKey { get; set; }
public required string ServiceToReload { get; set; }
}
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 required string Production { get; set; }
public required string Staging { get; set; }
private string? _production;
public string Production {
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 string CacheFolder { get; set; }
public required string AcmeFolder { get; set; }
public required Agent Agent { get; set; }
}

View File

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

View File

@ -10,6 +10,16 @@ var builder = WebApplication.CreateBuilder(args);
// Extract 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
var configurationSection = configuration.GetSection("Configuration");
var appSettings = configurationSection.Get<Configuration>() ?? throw new ArgumentNullException();

View File

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

View File

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

View File

@ -11,10 +11,13 @@
"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",
"AgentHostname": "",
"AgentPort": 9000,
"AgentKey": "UGnCaElLLJClHgUeet/yr7vNvPf13b1WkDJQMfsiP6I=",
"AgentKey": "",
"ServiceToReload": "haproxy"
}

View File

@ -22,15 +22,11 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- 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:
- D:\Compose\MaksIT.CertsUI\acme:/app/bin/Debug/net8.0/acme
- D:\Compose\MaksIT.CertsUI\cache:/app/bin/Debug/net8.0/cache
- D:\Compose\MaksIT.CertsUI\acme:/acme
- 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:
- 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 }}
{{- define "certs-ui.fullname" -}}
{{- $name := .Chart.Name -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- $rel := .Release.Name -}}
{{- if or (hasPrefix (printf "%s-" $name) $rel) (eq $rel $name) -}}
{{- if contains $name $rel -}}
{{- $rel | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" $rel $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end }}
{{- 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 := . -}}
{{- range $compName, $comp := .Values.components }}
---
@ -22,7 +24,7 @@ spec:
app.kubernetes.io/component: {{ $compName }}
{{- if and $comp.secretsFile $comp.secretsFile.forceUpdate }}
annotations:
"checksum/secrets-file": {{ (default "" $comp.secretsFile.content) | toString | sha256sum | quote }}
rollme: "{{$roll}}"
{{- end }}
spec:
{{- include "certs-ui.imagePullSecrets" $root | nindent 6 }}
@ -37,6 +39,8 @@ spec:
containerPort: {{ $tgt }}
{{- if $comp.env }}
env:
- name: ROLLOUT_TOKEN
value: "{{$roll}}"
{{- range $comp.env }}
- name: {{ .name }}
value: {{ .value | quote }}
@ -57,6 +61,14 @@ spec:
mountPath: {{ $comp.secretsFile.mountPath }}
subPath: {{ base $comp.secretsFile.mountPath }}
{{- 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 }}
{{- if or $hasVols $hasSecret }}
volumes:
@ -79,5 +91,13 @@ spec:
- key: {{ $comp.secretsFile.key }}
path: {{ base $comp.secretsFile.mountPath }}
{{- 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 }}

View File

@ -49,6 +49,34 @@ components:
keep: true
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:
replicas: 1
image: