mirror of
https://github.com/actions/runner.git
synced 2025-12-13 10:05:23 +00:00
GitHub Actions Runner
This commit is contained in:
231
src/Runner.Common/RunnerCertificateManager.cs
Normal file
231
src/Runner.Common/RunnerCertificateManager.cs
Normal file
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using GitHub.Services.Common;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Http;
|
||||
using GitHub.Services.WebApi;
|
||||
using GitHub.Runner.Sdk;
|
||||
|
||||
namespace GitHub.Runner.Common
|
||||
{
|
||||
[ServiceLocator(Default = typeof(RunnerCertificateManager))]
|
||||
public interface IRunnerCertificateManager : IRunnerService
|
||||
{
|
||||
bool SkipServerCertificateValidation { get; }
|
||||
string CACertificateFile { get; }
|
||||
string ClientCertificateFile { get; }
|
||||
string ClientCertificatePrivateKeyFile { get; }
|
||||
string ClientCertificateArchiveFile { get; }
|
||||
string ClientCertificatePassword { get; }
|
||||
IVssClientCertificateManager VssClientCertificateManager { get; }
|
||||
}
|
||||
|
||||
public class RunnerCertificateManager : RunnerService, IRunnerCertificateManager
|
||||
{
|
||||
private RunnerClientCertificateManager _runnerClientCertificateManager = new RunnerClientCertificateManager();
|
||||
|
||||
public bool SkipServerCertificateValidation { private set; get; }
|
||||
public string CACertificateFile { private set; get; }
|
||||
public string ClientCertificateFile { private set; get; }
|
||||
public string ClientCertificatePrivateKeyFile { private set; get; }
|
||||
public string ClientCertificateArchiveFile { private set; get; }
|
||||
public string ClientCertificatePassword { private set; get; }
|
||||
public IVssClientCertificateManager VssClientCertificateManager => _runnerClientCertificateManager;
|
||||
|
||||
public override void Initialize(IHostContext hostContext)
|
||||
{
|
||||
base.Initialize(hostContext);
|
||||
LoadCertificateSettings();
|
||||
}
|
||||
|
||||
// This should only be called from config
|
||||
public void SetupCertificate(bool skipCertValidation, string caCert, string clientCert, string clientCertPrivateKey, string clientCertArchive, string clientCertPassword)
|
||||
{
|
||||
Trace.Info("Setup runner certificate setting base on configuration inputs.");
|
||||
|
||||
if (skipCertValidation)
|
||||
{
|
||||
Trace.Info("Ignore SSL server certificate validation error");
|
||||
SkipServerCertificateValidation = true;
|
||||
VssClientHttpRequestSettings.Default.ServerCertificateValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(caCert))
|
||||
{
|
||||
ArgUtil.File(caCert, nameof(caCert));
|
||||
Trace.Info($"Self-Signed CA '{caCert}'");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(clientCert))
|
||||
{
|
||||
ArgUtil.File(clientCert, nameof(clientCert));
|
||||
ArgUtil.File(clientCertPrivateKey, nameof(clientCertPrivateKey));
|
||||
ArgUtil.File(clientCertArchive, nameof(clientCertArchive));
|
||||
|
||||
Trace.Info($"Client cert '{clientCert}'");
|
||||
Trace.Info($"Client cert private key '{clientCertPrivateKey}'");
|
||||
Trace.Info($"Client cert archive '{clientCertArchive}'");
|
||||
}
|
||||
|
||||
CACertificateFile = caCert;
|
||||
ClientCertificateFile = clientCert;
|
||||
ClientCertificatePrivateKeyFile = clientCertPrivateKey;
|
||||
ClientCertificateArchiveFile = clientCertArchive;
|
||||
ClientCertificatePassword = clientCertPassword;
|
||||
|
||||
_runnerClientCertificateManager.AddClientCertificate(ClientCertificateArchiveFile, ClientCertificatePassword);
|
||||
}
|
||||
|
||||
// This should only be called from config
|
||||
public void SaveCertificateSetting()
|
||||
{
|
||||
string certSettingFile = HostContext.GetConfigFile(WellKnownConfigFile.Certificates);
|
||||
IOUtil.DeleteFile(certSettingFile);
|
||||
|
||||
var setting = new RunnerCertificateSetting();
|
||||
if (SkipServerCertificateValidation)
|
||||
{
|
||||
Trace.Info($"Store Skip ServerCertificateValidation setting to '{certSettingFile}'");
|
||||
setting.SkipServerCertValidation = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(CACertificateFile))
|
||||
{
|
||||
Trace.Info($"Store CA cert setting to '{certSettingFile}'");
|
||||
setting.CACert = CACertificateFile;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ClientCertificateFile) &&
|
||||
!string.IsNullOrEmpty(ClientCertificatePrivateKeyFile) &&
|
||||
!string.IsNullOrEmpty(ClientCertificateArchiveFile))
|
||||
{
|
||||
Trace.Info($"Store client cert settings to '{certSettingFile}'");
|
||||
|
||||
setting.ClientCert = ClientCertificateFile;
|
||||
setting.ClientCertPrivatekey = ClientCertificatePrivateKeyFile;
|
||||
setting.ClientCertArchive = ClientCertificateArchiveFile;
|
||||
|
||||
if (!string.IsNullOrEmpty(ClientCertificatePassword))
|
||||
{
|
||||
string lookupKey = Guid.NewGuid().ToString("D").ToUpperInvariant();
|
||||
Trace.Info($"Store client cert private key password with lookup key {lookupKey}");
|
||||
|
||||
var credStore = HostContext.GetService<IRunnerCredentialStore>();
|
||||
credStore.Write($"GITHUB_ACTIONS_RUNNER_CLIENT_CERT_PASSWORD_{lookupKey}", "GitHub", ClientCertificatePassword);
|
||||
|
||||
setting.ClientCertPasswordLookupKey = lookupKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (SkipServerCertificateValidation ||
|
||||
!string.IsNullOrEmpty(CACertificateFile) ||
|
||||
!string.IsNullOrEmpty(ClientCertificateFile))
|
||||
{
|
||||
IOUtil.SaveObject(setting, certSettingFile);
|
||||
File.SetAttributes(certSettingFile, File.GetAttributes(certSettingFile) | FileAttributes.Hidden);
|
||||
}
|
||||
}
|
||||
|
||||
// This should only be called from unconfig
|
||||
public void DeleteCertificateSetting()
|
||||
{
|
||||
string certSettingFile = HostContext.GetConfigFile(WellKnownConfigFile.Certificates);
|
||||
if (File.Exists(certSettingFile))
|
||||
{
|
||||
Trace.Info($"Load runner certificate setting from '{certSettingFile}'");
|
||||
var certSetting = IOUtil.LoadObject<RunnerCertificateSetting>(certSettingFile);
|
||||
|
||||
if (certSetting != null && !string.IsNullOrEmpty(certSetting.ClientCertPasswordLookupKey))
|
||||
{
|
||||
Trace.Info("Delete client cert private key password from credential store.");
|
||||
var credStore = HostContext.GetService<IRunnerCredentialStore>();
|
||||
credStore.Delete($"GITHUB_ACTIONS_RUNNER_CLIENT_CERT_PASSWORD_{certSetting.ClientCertPasswordLookupKey}");
|
||||
}
|
||||
|
||||
Trace.Info($"Delete cert setting file: {certSettingFile}");
|
||||
IOUtil.DeleteFile(certSettingFile);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadCertificateSettings()
|
||||
{
|
||||
string certSettingFile = HostContext.GetConfigFile(WellKnownConfigFile.Certificates);
|
||||
if (File.Exists(certSettingFile))
|
||||
{
|
||||
Trace.Info($"Load runner certificate setting from '{certSettingFile}'");
|
||||
var certSetting = IOUtil.LoadObject<RunnerCertificateSetting>(certSettingFile);
|
||||
ArgUtil.NotNull(certSetting, nameof(RunnerCertificateSetting));
|
||||
|
||||
if (certSetting.SkipServerCertValidation)
|
||||
{
|
||||
Trace.Info("Ignore SSL server certificate validation error");
|
||||
SkipServerCertificateValidation = true;
|
||||
VssClientHttpRequestSettings.Default.ServerCertificateValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(certSetting.CACert))
|
||||
{
|
||||
// make sure all settings file exist
|
||||
ArgUtil.File(certSetting.CACert, nameof(certSetting.CACert));
|
||||
Trace.Info($"CA '{certSetting.CACert}'");
|
||||
CACertificateFile = certSetting.CACert;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(certSetting.ClientCert))
|
||||
{
|
||||
// make sure all settings file exist
|
||||
ArgUtil.File(certSetting.ClientCert, nameof(certSetting.ClientCert));
|
||||
ArgUtil.File(certSetting.ClientCertPrivatekey, nameof(certSetting.ClientCertPrivatekey));
|
||||
ArgUtil.File(certSetting.ClientCertArchive, nameof(certSetting.ClientCertArchive));
|
||||
|
||||
Trace.Info($"Client cert '{certSetting.ClientCert}'");
|
||||
Trace.Info($"Client cert private key '{certSetting.ClientCertPrivatekey}'");
|
||||
Trace.Info($"Client cert archive '{certSetting.ClientCertArchive}'");
|
||||
|
||||
ClientCertificateFile = certSetting.ClientCert;
|
||||
ClientCertificatePrivateKeyFile = certSetting.ClientCertPrivatekey;
|
||||
ClientCertificateArchiveFile = certSetting.ClientCertArchive;
|
||||
|
||||
if (!string.IsNullOrEmpty(certSetting.ClientCertPasswordLookupKey))
|
||||
{
|
||||
var cerdStore = HostContext.GetService<IRunnerCredentialStore>();
|
||||
ClientCertificatePassword = cerdStore.Read($"GITHUB_ACTIONS_RUNNER_CLIENT_CERT_PASSWORD_{certSetting.ClientCertPasswordLookupKey}").Password;
|
||||
HostContext.SecretMasker.AddValue(ClientCertificatePassword);
|
||||
}
|
||||
|
||||
_runnerClientCertificateManager.AddClientCertificate(ClientCertificateArchiveFile, ClientCertificatePassword);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Info("No certificate setting found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
internal class RunnerCertificateSetting
|
||||
{
|
||||
[DataMember]
|
||||
public bool SkipServerCertValidation { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public string CACert { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public string ClientCert { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public string ClientCertPrivatekey { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public string ClientCertArchive { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public string ClientCertPasswordLookupKey { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user