mirror of
https://github.com/MAKS-IT-COM/maksit-certs-ui.git
synced 2025-12-31 04:00:03 +01:00
(feature): dependencies update, improved logging, allign naming to k8s deployment
This commit is contained in:
parent
399415c6b8
commit
d59ded5cde
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MaksIT.LetsEncrypt.Models.Responses;
|
||||
public class AuthorizationChallengeChallenge
|
||||
@ -10,4 +11,13 @@ public class AuthorizationChallengeChallenge
|
||||
public string? Status { get; set; }
|
||||
|
||||
public string? Token { get; set; }
|
||||
|
||||
// New properties added to complete the model
|
||||
public DateTime? Validated { get; set; }
|
||||
|
||||
public AuthorizationChallengeError? Error { get; set; }
|
||||
|
||||
public List<AuthorizationChallengeValidationRecord>? ValidationRecord { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MaksIT.LetsEncrypt.Models.Responses;
|
||||
public class AuthorizationChallengeError {
|
||||
public string Type { get; set; }
|
||||
|
||||
public string Detail { get; set; }
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MaksIT.LetsEncrypt.Models.Responses;
|
||||
|
||||
|
||||
public class AuthorizationChallengeValidationRecord {
|
||||
public Uri? Url { get; set; }
|
||||
|
||||
public string? Hostname { get; set; }
|
||||
|
||||
public string? Port { get; set; }
|
||||
|
||||
public List<string>? AddressesResolved { get; set; }
|
||||
|
||||
public string? AddressUsed { get; set; }
|
||||
}
|
||||
@ -391,7 +391,7 @@ public class LetsEncryptService : ILetsEncryptService {
|
||||
|
||||
if (challenge?.Url == null) {
|
||||
_logger.LogError("Challenge URL is null");
|
||||
return Result.InternalServerError();
|
||||
return Result.InternalServerError("Challenge URL is null");
|
||||
}
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, challenge.Url);
|
||||
@ -712,6 +712,18 @@ public class LetsEncryptService : ILetsEncryptService {
|
||||
|
||||
throw new LetsEncrytException(problem, response);
|
||||
}
|
||||
|
||||
if (response.Content.Headers.ContentType?.MediaType == GetContentType(ContentType.Json)) {
|
||||
var authorizationChallengeChallenge = responseText.ToObject<AuthorizationChallengeChallenge>();
|
||||
|
||||
if (authorizationChallengeChallenge?.Status == "invalid") {
|
||||
throw new LetsEncrytException(new Problem {
|
||||
Type = authorizationChallengeChallenge.Error.Type,
|
||||
Detail = authorizationChallengeChallenge.Error.Detail,
|
||||
RawJson = responseText
|
||||
}, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SendResult<TResult> ProcessResponseContent<TResult>(HttpResponseMessage response, string responseText) {
|
||||
@ -743,14 +755,14 @@ public class LetsEncryptService : ILetsEncryptService {
|
||||
private Result HandleUnhandledException(Exception ex, string defaultMessage = "Let's Encrypt client unhandled exception") {
|
||||
List<string> messages = new() { defaultMessage };
|
||||
_logger.LogError(ex, messages.FirstOrDefault());
|
||||
messages.Add(ex.Message);
|
||||
ex.ExtractMessages().ForEach(m => messages.Add(m));
|
||||
return Result.InternalServerError([.. messages]);
|
||||
}
|
||||
|
||||
private Result<T?> HandleUnhandledException<T>(Exception ex, T? defaultValue = default, string defaultMessage = "Let's Encrypt client unhandled exception") {
|
||||
List<string> messages = new() { defaultMessage };
|
||||
_logger.LogError(ex, messages.FirstOrDefault());
|
||||
messages.Add(ex.Message);
|
||||
ex.ExtractMessages().ForEach(m => messages.Add(m));
|
||||
return Result<T?>.InternalServerError(defaultValue, [.. messages]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
using System.Net;
|
||||
|
||||
namespace MaksIT.LetsEncryptServer.Middlewares {
|
||||
public class GlobalExceptionMiddleware {
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<GlobalExceptionMiddleware> _logger;
|
||||
|
||||
public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger) {
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context) {
|
||||
try {
|
||||
await _next(context);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "An unhandled exception occurred.");
|
||||
await HandleExceptionAsync(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static Task HandleExceptionAsync(HttpContext context) {
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
context.Response.ContentType = "application/json";
|
||||
|
||||
var response = new { message = "An error occurred while processing your request." };
|
||||
return context.Response.WriteAsJsonAsync(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,10 @@
|
||||
using MaksIT.LetsEncryptServer;
|
||||
using MaksIT.LetsEncrypt.Services;
|
||||
using MaksIT.LetsEncryptServer.Services;
|
||||
using MaksIT.LetsEncryptServer.BackgroundServices;
|
||||
using MaksIT.LetsEncryptServer.Middlewares;
|
||||
using MaksIT.Core.Webapi.Middlewares;
|
||||
using MaksIT.Core.Logging;
|
||||
using MaksIT.LetsEncrypt.Extensions;
|
||||
using MaksIT.LetsEncrypt.Services;
|
||||
using MaksIT.LetsEncryptServer;
|
||||
using MaksIT.LetsEncryptServer.BackgroundServices;
|
||||
using MaksIT.LetsEncryptServer.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -24,6 +25,9 @@ if (File.Exists(secretsPath)) {
|
||||
var configurationSection = configuration.GetSection("Configuration");
|
||||
var appSettings = configurationSection.Get<Configuration>() ?? throw new ArgumentNullException();
|
||||
|
||||
// Add logging
|
||||
builder.Logging.AddConsoleLogger();
|
||||
|
||||
// Allow configurations to be available through IOptions<Configuration>
|
||||
builder.Services.Configure<Configuration>(configurationSection);
|
||||
|
||||
@ -55,11 +59,8 @@ if (app.Environment.IsDevelopment()) {
|
||||
app.UseSwaggerUI();
|
||||
app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
}
|
||||
else {
|
||||
// app.UseMiddleware<GlobalExceptionMiddleware>();
|
||||
}
|
||||
|
||||
app.UseMiddleware<GlobalExceptionMiddleware>();
|
||||
app.UseMiddleware<ErrorHandlingMiddleware>();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
|
||||
@ -81,9 +81,11 @@ public class CertsFlowService : ICertsFlowService {
|
||||
#region Internal methods
|
||||
public async Task<Result<Guid?>> ConfigureClientAsync(bool isStaging) {
|
||||
var sessionId = Guid.NewGuid();
|
||||
|
||||
var result = await _letsEncryptService.ConfigureClient(sessionId, isStaging);
|
||||
if (!result.IsSuccess)
|
||||
return result.ToResultOfType<Guid?>(default);
|
||||
|
||||
return Result<Guid?>.Ok(sessionId);
|
||||
}
|
||||
|
||||
@ -91,17 +93,22 @@ public class CertsFlowService : ICertsFlowService {
|
||||
RegistrationCache? cache = null;
|
||||
if (accountId == null) {
|
||||
accountId = Guid.NewGuid();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
var cacheResult = await _cacheService.LoadAccountFromCacheAsync(accountId.Value);
|
||||
|
||||
if (!cacheResult.IsSuccess || cacheResult.Value == null) {
|
||||
accountId = Guid.NewGuid();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
cache = cacheResult.Value;
|
||||
}
|
||||
|
||||
}
|
||||
var result = await _letsEncryptService.Init(sessionId, accountId.Value, description, contacts, cache);
|
||||
if (!result.IsSuccess)
|
||||
return result.ToResultOfType<Guid?>(default);
|
||||
|
||||
return Result<Guid?>.Ok(accountId.Value);
|
||||
}
|
||||
|
||||
@ -109,12 +116,15 @@ public class CertsFlowService : ICertsFlowService {
|
||||
var orderResult = await _letsEncryptService.NewOrder(sessionId, hostnames, challengeType);
|
||||
if (!orderResult.IsSuccess || orderResult.Value == null)
|
||||
return orderResult.ToResultOfType<List<string>?>(_ => null);
|
||||
|
||||
var challenges = new List<string>();
|
||||
|
||||
foreach (var kvp in orderResult.Value) {
|
||||
string[] splitToken = kvp.Value.Split('.');
|
||||
File.WriteAllText(Path.Combine(_acmePath, splitToken[0]), kvp.Value);
|
||||
challenges.Add(splitToken[0]);
|
||||
}
|
||||
|
||||
return Result<List<string>?>.Ok(challenges);
|
||||
}
|
||||
|
||||
@ -125,12 +135,15 @@ public class CertsFlowService : ICertsFlowService {
|
||||
return result;
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
var cacheResult = _letsEncryptService.GetRegistrationCache(sessionId);
|
||||
if (!cacheResult.IsSuccess || cacheResult.Value == null)
|
||||
return cacheResult;
|
||||
|
||||
var saveResult = await _cacheService.SaveToCacheAsync(cacheResult.Value.AccountId, cacheResult.Value);
|
||||
if (!saveResult.IsSuccess)
|
||||
return saveResult;
|
||||
|
||||
return Result.Ok();
|
||||
}
|
||||
|
||||
@ -142,20 +155,25 @@ public class CertsFlowService : ICertsFlowService {
|
||||
var cacheResult = _letsEncryptService.GetRegistrationCache(sessionId);
|
||||
if (!cacheResult.IsSuccess || cacheResult.Value?.CachedCerts == null)
|
||||
return cacheResult.ToResultOfType<Dictionary<string, string>?>(_ => null);
|
||||
|
||||
var results = new Dictionary<string, string>();
|
||||
foreach (var hostname in hostnames) {
|
||||
CertificateCache? cert;
|
||||
|
||||
if (cacheResult.Value.TryGetCachedCertificate(hostname, out cert)) {
|
||||
var content = $"{cert.Cert}\n{cert.PrivatePem}";
|
||||
results.Add(hostname, content);
|
||||
}
|
||||
}
|
||||
|
||||
var uploadResult = await _agentService.UploadCerts(results);
|
||||
if (!uploadResult.IsSuccess)
|
||||
return uploadResult.ToResultOfType<Dictionary<string, string>?>(default);
|
||||
|
||||
var reloadResult = await _agentService.ReloadService(_appSettings.Agent.ServiceToReload);
|
||||
if (!reloadResult.IsSuccess)
|
||||
return reloadResult.ToResultOfType<Dictionary<string, string>?>(default);
|
||||
|
||||
return Result<Dictionary<string, string>?>.Ok(results);
|
||||
}
|
||||
|
||||
@ -165,12 +183,15 @@ public class CertsFlowService : ICertsFlowService {
|
||||
if (!result.IsSuccess)
|
||||
return result;
|
||||
}
|
||||
|
||||
var cacheResult = _letsEncryptService.GetRegistrationCache(sessionId);
|
||||
if (!cacheResult.IsSuccess || cacheResult.Value == null)
|
||||
return cacheResult;
|
||||
|
||||
var saveResult = await _cacheService.SaveToCacheAsync(cacheResult.Value.AccountId, cacheResult.Value);
|
||||
if (!saveResult.IsSuccess)
|
||||
return saveResult;
|
||||
|
||||
return Result.Ok();
|
||||
}
|
||||
|
||||
@ -194,6 +215,7 @@ public class CertsFlowService : ICertsFlowService {
|
||||
if (!challengeResult.IsSuccess)
|
||||
return challengeResult.ToResultOfType<Guid?>(default);
|
||||
}
|
||||
|
||||
var getOrderResult = await GetOrderAsync(sessionId, hostnames);
|
||||
if (!getOrderResult.IsSuccess)
|
||||
return getOrderResult.ToResultOfType<Guid?>(default);
|
||||
@ -203,10 +225,10 @@ public class CertsFlowService : ICertsFlowService {
|
||||
return certsResult.ToResultOfType<Guid?>(default);
|
||||
|
||||
// Bypass applying certificates in staging mode
|
||||
if (!isStaging) {
|
||||
var applyCertsResult = await ApplyCertificatesAsync(sessionId, hostnames);
|
||||
if (!applyCertsResult.IsSuccess)
|
||||
return applyCertsResult.ToResultOfType<Guid?>(_ => null);
|
||||
if (!isStaging) {
|
||||
var applyCertsResult = await ApplyCertificatesAsync(sessionId, hostnames);
|
||||
if (!applyCertsResult.IsSuccess)
|
||||
return applyCertsResult.ToResultOfType<Guid?>(_ => null);
|
||||
}
|
||||
|
||||
return Result<Guid?>.Ok(initResult.Value);
|
||||
@ -222,9 +244,11 @@ public class CertsFlowService : ICertsFlowService {
|
||||
var initResult = await InitAsync(sessionId, accountId, description, contacts);
|
||||
if (!initResult.IsSuccess)
|
||||
return initResult;
|
||||
|
||||
var revokeResult = await RevokeCertificatesAsync(sessionId, hostnames);
|
||||
if (!revokeResult.IsSuccess)
|
||||
return revokeResult;
|
||||
|
||||
return Result.Ok();
|
||||
}
|
||||
#endregion
|
||||
@ -255,9 +279,11 @@ public class CertsFlowService : ICertsFlowService {
|
||||
#region Acme Challenge REST methods
|
||||
public Result<string?> AcmeChallenge(string fileName) {
|
||||
DeleteExporedChallenges();
|
||||
|
||||
var challengePath = Path.Combine(_acmePath, fileName);
|
||||
if(!File.Exists(challengePath))
|
||||
if (!File.Exists(challengePath))
|
||||
return Result<string?>.NotFound(null);
|
||||
|
||||
var fileContent = File.ReadAllText(Path.Combine(_acmePath, fileName));
|
||||
return Result<string?>.Ok(fileContent);
|
||||
}
|
||||
@ -268,6 +294,7 @@ public class CertsFlowService : ICertsFlowService {
|
||||
try {
|
||||
var creationTime = File.GetCreationTime(file);
|
||||
var timeDifference = currentDate - creationTime;
|
||||
|
||||
if (timeDifference.TotalDays > 1) {
|
||||
File.Delete(file);
|
||||
_logger.LogInformation($"Deleted file: {file}");
|
||||
|
||||
@ -4,21 +4,21 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
- letsencrypt-app
|
||||
- letsencrypt-server
|
||||
- certs-ui-client
|
||||
- certs-ui-server
|
||||
networks:
|
||||
- maks-it
|
||||
|
||||
letsencrypt-app:
|
||||
container_name: letsencrypt-app
|
||||
certs-ui-client:
|
||||
container_name: certs-ui-client
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- LETSENCRYPT_SERVER=http://localhost:8080
|
||||
networks:
|
||||
- maks-it
|
||||
|
||||
letsencrypt-server:
|
||||
container_name: letsencrypt-server
|
||||
certs-ui-server:
|
||||
container_name: certs-ui-server
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_HTTP_PORTS=5000
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
services:
|
||||
letsencrypt-app:
|
||||
image: ${DOCKER_REGISTRY-}letsencrypt-app
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ClientApp/Dockerfile
|
||||
|
||||
reverse-proxy:
|
||||
image: ${DOCKER_REGISTRY-}reverse-proxy
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ReverseProxy/Dockerfile
|
||||
|
||||
letsencrypt-server:
|
||||
image: ${DOCKER_REGISTRY-}letsencrypt-server
|
||||
certs-ui-client:
|
||||
image: ${DOCKER_REGISTRY-}certs-ui-client
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ClientApp/Dockerfile
|
||||
|
||||
certs-ui-server:
|
||||
image: ${DOCKER_REGISTRY-}certs-ui-server
|
||||
build:
|
||||
context: .
|
||||
dockerfile: LetsEncryptServer/Dockerfile
|
||||
|
||||
Loading…
Reference in New Issue
Block a user