mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
232 lines
10 KiB
C#
232 lines
10 KiB
C#
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; }
|
|
}
|
|
}
|