mirror of
https://github.com/MAKS-IT-COM/maksit-certs-ui.git
synced 2025-12-31 04:00:03 +01:00
Merge branch 'master' of gitlab:hailstrike/letsencrypt
This commit is contained in:
commit
0e8dace2ab
2
v2.0/LetsEncrypt/.vscode/launch.json
vendored
2
v2.0/LetsEncrypt/.vscode/launch.json
vendored
@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/bin/Debug/netcoreapp2.2/LetsEncrypt.dll",
|
||||
"program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/LetsEncrypt.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
|
||||
@ -7,6 +7,8 @@ using System.Linq;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Mono.Unix;
|
||||
|
||||
using LetsEncrypt.Services;
|
||||
using LetsEncrypt.Helpers;
|
||||
using LetsEncrypt.Entities;
|
||||
@ -20,11 +22,13 @@ namespace LetsEncrypt
|
||||
private readonly AppSettings _appSettings;
|
||||
private readonly ILetsEncryptService _letsEncryptService;
|
||||
private readonly IKeyService _keyService;
|
||||
private readonly ITerminalService _terminalService;
|
||||
|
||||
public App(IOptions<AppSettings> appSettings, ILetsEncryptService letsEncryptService, IKeyService keyService) {
|
||||
public App(IOptions<AppSettings> appSettings, ILetsEncryptService letsEncryptService, IKeyService keyService, ITerminalService terminalService) {
|
||||
_appSettings = appSettings.Value;
|
||||
_letsEncryptService = letsEncryptService;
|
||||
_keyService = keyService;
|
||||
_terminalService = terminalService;
|
||||
}
|
||||
|
||||
public void Run() {
|
||||
@ -46,7 +50,7 @@ namespace LetsEncrypt
|
||||
|
||||
try {
|
||||
//define cache folder
|
||||
string cache = Path.Combine(AppPath, "cache", customer.id);
|
||||
string cache = Path.Combine(AppPath, env.cache, customer.id);
|
||||
if(!Directory.Exists(cache)) {
|
||||
Directory.CreateDirectory(cache);
|
||||
}
|
||||
@ -70,16 +74,16 @@ namespace LetsEncrypt
|
||||
CachedCertificateResult certRes = new CachedCertificateResult();
|
||||
if (_letsEncryptService.TryGetCachedCertificate(site.name, out certRes)) {
|
||||
string cert = Path.Combine(ssl, site.name + ".crt");
|
||||
if(!File.Exists(cert))
|
||||
//if(!File.Exists(cert))
|
||||
File.WriteAllText(cert, certRes.Certificate);
|
||||
|
||||
string key = Path.Combine(ssl, site.name + ".key");
|
||||
if(!File.Exists(key)) {
|
||||
//if(!File.Exists(key)) {
|
||||
using (StreamWriter writer = File.CreateText(key))
|
||||
_keyService.ExportPrivateKey(certRes.PrivateKey, writer);
|
||||
}
|
||||
//}
|
||||
|
||||
Console.WriteLine("Certificate and Key exists and valid.");
|
||||
Console.WriteLine("Certificate and Key exists and valid. Restored from cache.");
|
||||
}
|
||||
else {
|
||||
//new nonce
|
||||
@ -103,8 +107,11 @@ namespace LetsEncrypt
|
||||
throw new DirectoryNotFoundException(string.Format("Directory {0} wasn't created", acme));
|
||||
}
|
||||
|
||||
foreach (FileInfo file in new DirectoryInfo(acme).GetFiles())
|
||||
foreach (FileInfo file in new DirectoryInfo(acme).GetFiles()) {
|
||||
if(file.LastWriteTimeUtc < DateTime.UtcNow.AddMonths(-3))
|
||||
file.Delete();
|
||||
}
|
||||
|
||||
|
||||
foreach (var result in orders.Result)
|
||||
{
|
||||
@ -115,6 +122,9 @@ namespace LetsEncrypt
|
||||
File.WriteAllText(token, splitToken[1]);
|
||||
}
|
||||
|
||||
_terminalService.Exec("chgrp -R nginx /var/www");
|
||||
_terminalService.Exec("chmod -R g+rwx /var/www");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -175,6 +185,10 @@ namespace LetsEncrypt
|
||||
Console.WriteLine(ex.Message.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_terminalService.Exec("systemctl restart nginx");
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Console.WriteLine(ex.Message.ToString());
|
||||
|
||||
@ -7,8 +7,8 @@ namespace LetsEncrypt.Entities
|
||||
|
||||
public class JwsMessage
|
||||
{
|
||||
[JsonProperty("header")]
|
||||
public JwsHeader Header { get; set; }
|
||||
//[JsonProperty("header")]
|
||||
//public JwsHeader Header { get; set; }
|
||||
|
||||
[JsonProperty("protected")]
|
||||
public string Protected { get; set; }
|
||||
|
||||
@ -9,6 +9,7 @@ namespace LetsEncrypt.Helpers
|
||||
public class Environment {
|
||||
public string name { get; set; }
|
||||
public string url { get; set; }
|
||||
public string cache { get; set; }
|
||||
public string www { get; set; }
|
||||
public string acme { get; set; }
|
||||
public string ssl { get; set; }
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -13,6 +13,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="Newtonsoft.JSON" Version="12.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ namespace LetsEncrypt
|
||||
services.AddScoped<IKeyService, KeyService>();
|
||||
services.AddScoped<IJwsService, JwsService>();
|
||||
services.AddScoped<ILetsEncryptService, LetsEncryptService>();
|
||||
services.AddScoped<ITerminalService,TerminalService>();
|
||||
|
||||
// add app
|
||||
services.AddTransient<App>();
|
||||
|
||||
@ -57,10 +57,31 @@ namespace LetsEncrypt.Services
|
||||
|
||||
var message = new JwsMessage
|
||||
{
|
||||
Payload = Base64UrlEncoded(JsonConvert.SerializeObject(payload)),
|
||||
Payload = "",
|
||||
Protected = Base64UrlEncoded(JsonConvert.SerializeObject(protectedHeader))
|
||||
};
|
||||
|
||||
if(payload != null) {
|
||||
if(payload is String) {
|
||||
string value = payload.ToString();
|
||||
switch(value) {
|
||||
case "POST-as-GET":
|
||||
message.Payload = string.Empty;
|
||||
break;
|
||||
|
||||
default:
|
||||
message.Payload = Base64UrlEncoded(value);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
message.Payload = Base64UrlEncoded(JsonConvert.SerializeObject(payload));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
message.Signature = Base64UrlEncoded(
|
||||
_rsa.SignData(Encoding.ASCII.GetBytes(message.Protected + "." + message.Payload),
|
||||
HashAlgorithmName.SHA256,
|
||||
|
||||
@ -1,3 +1,10 @@
|
||||
/**
|
||||
* tools.itef.org/html/draft-itef-acme-acme-18
|
||||
* https://community.letsencrypt.org/t/trying-to-do-post-as-get-but-getting-post-jws-not-signed/108371
|
||||
* https://tools.ietf.org/html/rfc8555#section-6.2
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
using System.Threading;
|
||||
@ -125,15 +132,17 @@ namespace LetsEncrypt.Services {
|
||||
|
||||
//New Account request
|
||||
_jwsService.Init(_accountKey, null);
|
||||
var (account, response) = await SendAsync<Account>(HttpMethod.Post, _directory.NewAccount, new Account
|
||||
|
||||
var letsEncryptOrder = new Account
|
||||
{
|
||||
// we validate this in the UI before we get here, so that is fine
|
||||
TermsOfServiceAgreed = true,
|
||||
Contacts = contacts.Select(contact =>
|
||||
string.Format("mailto:{0}", contact)
|
||||
).ToArray()
|
||||
};
|
||||
|
||||
}, token);
|
||||
var (account, response) = await SendAsync<Account>(HttpMethod.Post, _directory.NewAccount, letsEncryptOrder, token);
|
||||
_jwsService.SetKeyId(account);
|
||||
|
||||
if (account.Status != "valid")
|
||||
@ -195,7 +204,7 @@ namespace LetsEncrypt.Services {
|
||||
//update jws with account url
|
||||
_jwsService.Init(_accountKey, _cache.Location.ToString());
|
||||
|
||||
var (order, response) = await SendAsync<Order>(HttpMethod.Post, _directory.NewOrder, new Order
|
||||
var letsEncryptOrder = new Order
|
||||
{
|
||||
Expires = DateTime.UtcNow.AddDays(2),
|
||||
Identifiers = hostnames.Select(hostname => new OrderIdentifier
|
||||
@ -203,7 +212,9 @@ namespace LetsEncrypt.Services {
|
||||
Type = "dns",
|
||||
Value = hostname
|
||||
}).ToArray()
|
||||
}, token);
|
||||
};
|
||||
|
||||
var (order, response) = await SendAsync<Order>(HttpMethod.Post, _directory.NewOrder, letsEncryptOrder, token);
|
||||
|
||||
if (order.Status != "pending")
|
||||
throw new InvalidOperationException("Created new order and expected status 'pending', but got: " + order.Status + Environment.NewLine +
|
||||
@ -213,7 +224,8 @@ namespace LetsEncrypt.Services {
|
||||
var results = new Dictionary<string, string>();
|
||||
foreach (var item in order.Authorizations)
|
||||
{
|
||||
var (challengeResponse, responseText) = await SendAsync<AuthorizationChallengeResponse>(HttpMethod.Get, item, null, token);
|
||||
var (challengeResponse, responseText) = await SendAsync<AuthorizationChallengeResponse>(HttpMethod.Post, item, "POST-as-GET", token);
|
||||
|
||||
if (challengeResponse.Status == "valid")
|
||||
continue;
|
||||
|
||||
@ -277,8 +289,12 @@ namespace LetsEncrypt.Services {
|
||||
{
|
||||
_jwsService.Init(_accountKey, _cache.Location.ToString());
|
||||
|
||||
|
||||
|
||||
|
||||
for (var index = 0; index < _challenges.Count; index++)
|
||||
{
|
||||
|
||||
var challenge = _challenges[index];
|
||||
|
||||
while (true)
|
||||
@ -288,6 +304,7 @@ namespace LetsEncrypt.Services {
|
||||
switch (challenge.Type) {
|
||||
case "dns-01": {
|
||||
authorizeChallenge.KeyAuthorization = _jwsService.GetKeyAuthorization(challenge.Token);
|
||||
//var (result, responseText) = await SendAsync<AuthorizationChallengeResponse>(HttpMethod.Post, challenge.Url, authorizeChallenge, token);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -296,7 +313,7 @@ namespace LetsEncrypt.Services {
|
||||
}
|
||||
}
|
||||
|
||||
var (result, responseText) = await SendAsync<AuthorizationChallengeResponse>(HttpMethod.Post, challenge.Url, authorizeChallenge, token);
|
||||
var (result, responseText) = await SendAsync<AuthorizationChallengeResponse>(HttpMethod.Post, challenge.Url, "{}", token);
|
||||
|
||||
if (result.Status == "valid")
|
||||
break;
|
||||
@ -304,8 +321,10 @@ namespace LetsEncrypt.Services {
|
||||
throw new InvalidOperationException("Failed autorization of " + _currentOrder.Identifiers[index].Value + Environment.NewLine + responseText);
|
||||
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,7 +336,7 @@ namespace LetsEncrypt.Services {
|
||||
//update jws
|
||||
_jwsService.Init(_accountKey, _cache.Location.ToString());
|
||||
|
||||
var (order, response) = await SendAsync<Order>(HttpMethod.Post, _directory.NewOrder, new Order
|
||||
var letsEncryptOrder = new Order
|
||||
{
|
||||
Expires = DateTime.UtcNow.AddDays(2),
|
||||
Identifiers = hostnames.Select(hostname => new OrderIdentifier
|
||||
@ -325,7 +344,9 @@ namespace LetsEncrypt.Services {
|
||||
Type = "dns",
|
||||
Value = hostname
|
||||
}).ToArray()
|
||||
}, token);
|
||||
};
|
||||
|
||||
var (order, response) = await SendAsync<Order>(HttpMethod.Post, _directory.NewOrder, letsEncryptOrder, token);
|
||||
|
||||
_currentOrder = order;
|
||||
}
|
||||
@ -351,14 +372,16 @@ namespace LetsEncrypt.Services {
|
||||
|
||||
csr.CertificateExtensions.Add(san.Build());
|
||||
|
||||
var (response, responseText) = await SendAsync<Order>(HttpMethod.Post, _currentOrder.Finalize, new FinalizeRequest
|
||||
var letsEncryptOrder = new FinalizeRequest
|
||||
{
|
||||
CSR = _jwsService.Base64UrlEncoded(csr.CreateSigningRequest())
|
||||
}, token);
|
||||
};
|
||||
|
||||
var (response, responseText) = await SendAsync<Order>(HttpMethod.Post, _currentOrder.Finalize, letsEncryptOrder, token);
|
||||
|
||||
while (response.Status != "valid")
|
||||
{
|
||||
(response, responseText) = await SendAsync<Order>(HttpMethod.Get, response.Location, null, token);
|
||||
(response, responseText) = await SendAsync<Order>(HttpMethod.Post, response.Location, "POST-as-GET", token);
|
||||
|
||||
if(response.Status == "processing")
|
||||
{
|
||||
@ -368,7 +391,7 @@ namespace LetsEncrypt.Services {
|
||||
throw new InvalidOperationException("Invalid order status: " + response.Status + Environment.NewLine +
|
||||
responseText);
|
||||
}
|
||||
var (pem, _) = await SendAsync<string>(HttpMethod.Get, response.Certificate, null, token);
|
||||
var (pem, _) = await SendAsync<string>(HttpMethod.Post, response.Certificate, "POST-as-GET", token);
|
||||
|
||||
var cert = new X509Certificate2(Encoding.UTF8.GetBytes(pem));
|
||||
|
||||
@ -450,6 +473,8 @@ namespace LetsEncrypt.Services {
|
||||
request.Content.Headers.Add("Content-Type", requestType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var response = await _client.SendAsync(request, token).ConfigureAwait(false);
|
||||
|
||||
if (method == HttpMethod.Post)
|
||||
|
||||
31
v2.0/LetsEncrypt/Services/TerminalService.cs
Normal file
31
v2.0/LetsEncrypt/Services/TerminalService.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace LetsEncrypt {
|
||||
|
||||
public interface ITerminalService {
|
||||
void Exec(string cmd);
|
||||
}
|
||||
|
||||
public class TerminalService : ITerminalService {
|
||||
|
||||
public void Exec(string cmd) {
|
||||
var escapedArgs = cmd.Replace("\"", "\\\"");
|
||||
|
||||
var pc = new Process {
|
||||
StartInfo = new ProcessStartInfo {
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
FileName = "/bin/bash",
|
||||
Arguments = $"-c \"{escapedArgs}\""
|
||||
}
|
||||
};
|
||||
|
||||
pc.Start();
|
||||
pc.WaitForExit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
74
v2.0/LetsEncrypt/appsettings copy.json
Normal file
74
v2.0/LetsEncrypt/appsettings copy.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"AppSettings": {
|
||||
|
||||
"active": "StagingV2",
|
||||
|
||||
"environments": [
|
||||
{
|
||||
"name": "StagingV2",
|
||||
"url": "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||
|
||||
"www": "/var/www",
|
||||
"acme": ".well-known/acme-challenge",
|
||||
"ssl": "/home/maksym/source/temp"
|
||||
},
|
||||
{
|
||||
"name": "ProductionV2",
|
||||
"url": "https://acme-v02.api.letsencrypt.org/directory",
|
||||
|
||||
"www": "/var/www",
|
||||
"acme": ".well-known/acme-challenge",
|
||||
"ssl": "/etc/nginx/ssl"
|
||||
}
|
||||
],
|
||||
|
||||
"customers": [
|
||||
{
|
||||
"id": "9b4c8584-dc83-4388-b45f-2942e34dca9d",
|
||||
"contacts": [ "maksym.sadovnychyy@gmail.com" ],
|
||||
"name": "Maksym",
|
||||
"lastname": "Sadovnychyy",
|
||||
|
||||
"sites": [
|
||||
{
|
||||
"name": "maks-it.com",
|
||||
"hosts": [
|
||||
"maks-it.com",
|
||||
"www.maks-it.com",
|
||||
"it.maks-it.com",
|
||||
"www.it.maks-it.com",
|
||||
"ru.maks-it.com",
|
||||
"www.ru.maks-it.com",
|
||||
"api.maks-it.com",
|
||||
"www.api.maks-it.com"
|
||||
],
|
||||
"challenge": "http-01"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d6be989c-3b68-480d-9f4f-b7317674847a",
|
||||
"contacts": [ "anastasiia.pavlovskaia@gmail.com" ],
|
||||
|
||||
"name": "Anastasiia",
|
||||
"lastname": "Pavlovskaia",
|
||||
|
||||
"sites": [
|
||||
{
|
||||
|
||||
"name": "nastyarey.com",
|
||||
"hosts": [
|
||||
"nastyarey.com",
|
||||
"www.nastyarey.com",
|
||||
"it.nastyarey.com",
|
||||
"www.it.nastyarey.com",
|
||||
"ru.nastyarey.com",
|
||||
"www.ru.nastyarey.com"
|
||||
],
|
||||
"challenge": "http-01"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,31 +1,33 @@
|
||||
{
|
||||
"AppSettings": {
|
||||
|
||||
"active": "ProductionV2",
|
||||
"active": "StagingV2",
|
||||
|
||||
"environments": [
|
||||
{
|
||||
"name": "StagingV2",
|
||||
"url": "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||
|
||||
"cache": "staging_cache",
|
||||
"www": "/var/www",
|
||||
"acme": ".well-known/acme-challenge",
|
||||
"ssl": "/etc/nginx/ssl"
|
||||
"ssl": "/home/maksym/source/temp"
|
||||
},
|
||||
{
|
||||
"name": "ProductionV2",
|
||||
"url": "https://acme-v02.api.letsencrypt.org/directory",
|
||||
|
||||
"cache": "production_cache",
|
||||
"www": "/var/www",
|
||||
"acme": ".well-known/acme-challenge",
|
||||
"ssl": "/etc/nginx/ssl"
|
||||
"ssl": "/etc/nginx/ssl/"
|
||||
}
|
||||
],
|
||||
|
||||
"customers": [
|
||||
{
|
||||
"id": "9b4c8584-dc83-4388-b45f-2942e34dca9d",
|
||||
"contacts": [ "maksym.sadovnychyy@gmail.com" ],
|
||||
"contacts": [ "maksym.sadovnychyy@google.com" ],
|
||||
"name": "Maksym",
|
||||
"lastname": "Sadovnychyy",
|
||||
|
||||
@ -50,29 +52,6 @@
|
||||
"challenge": "http-01"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d6be989c-3b68-480d-9f4f-b7317674847a",
|
||||
"contacts": [ "anastasiia.pavlovskaia@gmail.com" ],
|
||||
|
||||
"name": "Anastasiia",
|
||||
"lastname": "Pavlovskaia",
|
||||
|
||||
"sites": [
|
||||
{
|
||||
|
||||
"name": "nastyarey.com",
|
||||
"hosts": [
|
||||
"nastyarey.com",
|
||||
"www.nastyarey.com",
|
||||
"it.nastyarey.com",
|
||||
"www.it.nastyarey.com",
|
||||
"ru.nastyarey.com",
|
||||
"www.ru.nastyarey.com"
|
||||
],
|
||||
"challenge": "http-01"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user