Skip to content

Commit

Permalink
Adds cert setup for macOS. Closes #49
Browse files Browse the repository at this point in the history
  • Loading branch information
waldekmastykarz committed Dec 16, 2023
1 parent d97a798 commit 20107fe
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 2 deletions.
7 changes: 7 additions & 0 deletions dev-proxy/ProxyCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class ProxyCommandHandler : ICommandHandler {
public Option<IEnumerable<int>?> WatchPids { get; set; }
public Option<IEnumerable<string>?> WatchProcessNames { get; set; }
public Option<int?> Rate { get; set; }
public Option<bool?> NoFirstRun { get; set; }

private readonly PluginEvents _pluginEvents;
private readonly ISet<UrlToWatch> _urlsToWatch;
Expand All @@ -28,6 +29,7 @@ public ProxyCommandHandler(Option<int?> port,
Option<IEnumerable<int>?> watchPids,
Option<IEnumerable<string>?> watchProcessNames,
Option<int?> rate,
Option<bool?> noFirstRun,
PluginEvents pluginEvents,
ISet<UrlToWatch> urlsToWatch,
ILogger logger) {
Expand All @@ -38,6 +40,7 @@ public ProxyCommandHandler(Option<int?> port,
WatchPids = watchPids ?? throw new ArgumentNullException(nameof(watchPids));
WatchProcessNames = watchProcessNames ?? throw new ArgumentNullException(nameof(watchProcessNames));
Rate = rate ?? throw new ArgumentNullException(nameof(rate));
NoFirstRun = noFirstRun ?? throw new ArgumentNullException(nameof(noFirstRun));
_pluginEvents = pluginEvents ?? throw new ArgumentNullException(nameof(pluginEvents));
_urlsToWatch = urlsToWatch ?? throw new ArgumentNullException(nameof(urlsToWatch));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
Expand Down Expand Up @@ -76,6 +79,10 @@ public async Task<int> InvokeAsync(InvocationContext context) {
if (rate is not null) {
Configuration.Rate = rate.Value;
}
var noFirstRun = context.ParseResult.GetValueForOption(NoFirstRun);
if (noFirstRun is not null) {
Configuration.NoFirstRun = noFirstRun.Value;
}

CancellationToken? cancellationToken = (CancellationToken?)context.BindingContext.GetService(typeof(CancellationToken?));

Expand Down
1 change: 1 addition & 0 deletions dev-proxy/ProxyConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class ProxyConfiguration: IProxyConfiguration {
public IEnumerable<string> WatchProcessNames { get; set; } = new List<string>();
[JsonPropertyName("rate")]
public int Rate { get; set; } = 50;
public bool NoFirstRun { get; set; } = false;
public string ConfigFile { get; set; } = "devproxyrc.json";
}

44 changes: 44 additions & 0 deletions dev-proxy/ProxyEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public async Task Run(CancellationToken? cancellationToken) {

_proxyServer = new ProxyServer();

_proxyServer.CertificateManager.RootCertificateName = "Dev Proxy CA";
_proxyServer.CertificateManager.CertificateStorage = new CertificateDiskCache();
_proxyServer.BeforeRequest += OnRequest;
_proxyServer.BeforeResponse += OnBeforeResponse;
Expand All @@ -85,6 +86,9 @@ public async Task Run(CancellationToken? cancellationToken) {
_proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
cancellationToken?.Register(OnCancellation);

// run first-run setup on macOS
FirstRunSetup();

var ipAddress = string.IsNullOrEmpty(_config.IPAddress) ? IPAddress.Any : IPAddress.Parse(_config.IPAddress);
_explicitEndPoint = new ExplicitProxyEndPoint(ipAddress, _config.Port, true);
if (!RunTime.IsWindows) {
Expand Down Expand Up @@ -132,6 +136,46 @@ public async Task Run(CancellationToken? cancellationToken) {
while (_proxyServer.ProxyRunning) { await Task.Delay(10); }
}

private void FirstRunSetup()
{
if (!RunTime.IsMac ||
_config.NoFirstRun ||
!IsFirstRun())
{
return;
}

var bashScriptPath = Path.Join(ProxyUtils.AppFolder, "trust-cert.sh");
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "/bin/bash",
Arguments = bashScriptPath,
UseShellExecute = true,
CreateNoWindow = false
};

var process = new Process() { StartInfo = startInfo };
process.Start();
process.WaitForExit();
}

private bool IsFirstRun()
{
var firstRunFilePath = Path.Combine(ProxyUtils.AppFolder!, ".hasrun");
if (File.Exists(firstRunFilePath))
{
return false;
}

try
{
File.WriteAllText(firstRunFilePath, "");
}
catch {}

return true;
}

private void AfterRequestLog(object? sender, RequestLogArgs e) {
if (!_isRecording)
{
Expand Down
8 changes: 6 additions & 2 deletions dev-proxy/ProxyHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal class ProxyHost
private Option<IEnumerable<string>?> _watchProcessNamesOption;
private static Option<string?>? _configFileOption;
private Option<int?> _rateOption;
private Option<bool?> _noFirstRunOption;

private static bool _configFileResolved = false;
private static string _configFile = "devproxyrc.json";
Expand Down Expand Up @@ -175,6 +176,8 @@ public ProxyHost() {
}
});

_noFirstRunOption = new Option<bool?>("--no-first-run", "Skip the first run experience");

ProxyCommandHandler.Configuration.ConfigFile = ConfigFile;
}

Expand All @@ -192,7 +195,8 @@ public RootCommand GetRootCommand(ILogger logger)
_rateOption,
// _configFileOption is set during the call to load
// `ProxyCommandHandler.Configuration`. As such, it's always set here
_configFileOption!
_configFileOption!,
_noFirstRunOption
};
command.Description = "Dev Proxy is a command line tool for testing Microsoft Graph, SharePoint Online and any other HTTP APIs.";

Expand All @@ -205,6 +209,6 @@ public RootCommand GetRootCommand(ILogger logger)
return command;
}

public ProxyCommandHandler GetCommandHandler(PluginEvents pluginEvents, ISet<UrlToWatch> urlsToWatch, ILogger logger) => new ProxyCommandHandler(_portOption, _ipAddressOption, _logLevelOption, _recordOption, _watchPidsOption, _watchProcessNamesOption, _rateOption, pluginEvents, urlsToWatch, logger);
public ProxyCommandHandler GetCommandHandler(PluginEvents pluginEvents, ISet<UrlToWatch> urlsToWatch, ILogger logger) => new ProxyCommandHandler(_portOption, _ipAddressOption, _logLevelOption, _recordOption, _watchPidsOption, _watchProcessNamesOption, _rateOption, _noFirstRunOption, pluginEvents, urlsToWatch, logger);

Check warning on line 212 in dev-proxy/ProxyHost.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'logLevel' in 'ProxyCommandHandler.ProxyCommandHandler(Option<int?> port, Option<string?> ipAddress, Option<LogLevel?> logLevel, Option<bool?> record, Option<IEnumerable<int>?> watchPids, Option<IEnumerable<string>?> watchProcessNames, Option<int?> rate, Option<bool?> noFirstRun, PluginEvents pluginEvents, ISet<UrlToWatch> urlsToWatch, ILogger logger)'.

Check warning on line 212 in dev-proxy/ProxyHost.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'logLevel' in 'ProxyCommandHandler.ProxyCommandHandler(Option<int?> port, Option<string?> ipAddress, Option<LogLevel?> logLevel, Option<bool?> record, Option<IEnumerable<int>?> watchPids, Option<IEnumerable<string>?> watchProcessNames, Option<int?> rate, Option<bool?> noFirstRun, PluginEvents pluginEvents, ISet<UrlToWatch> urlsToWatch, ILogger logger)'.

Check warning on line 212 in dev-proxy/ProxyHost.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Possible null reference argument for parameter 'logLevel' in 'ProxyCommandHandler.ProxyCommandHandler(Option<int?> port, Option<string?> ipAddress, Option<LogLevel?> logLevel, Option<bool?> record, Option<IEnumerable<int>?> watchPids, Option<IEnumerable<string>?> watchProcessNames, Option<int?> rate, Option<bool?> noFirstRun, PluginEvents pluginEvents, ISet<UrlToWatch> urlsToWatch, ILogger logger)'.

Check warning on line 212 in dev-proxy/ProxyHost.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Possible null reference argument for parameter 'logLevel' in 'ProxyCommandHandler.ProxyCommandHandler(Option<int?> port, Option<string?> ipAddress, Option<LogLevel?> logLevel, Option<bool?> record, Option<IEnumerable<int>?> watchPids, Option<IEnumerable<string>?> watchProcessNames, Option<int?> rate, Option<bool?> noFirstRun, PluginEvents pluginEvents, ISet<UrlToWatch> urlsToWatch, ILogger logger)'.
}

3 changes: 3 additions & 0 deletions dev-proxy/dev-proxy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
<None Update="responses.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="trust-cert.sh">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="responses.sample.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
23 changes: 23 additions & 0 deletions dev-proxy/trust-cert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
set -e

echo -e "\nDev Proxy uses a self-signed certificate to intercept and inspect HTTPS traffic.\nUpdate the certificate in your Keychain so that it's trusted by your browser? (Y/n)? \c"
read -n 1 answer

if [ "$answer" = "n" ]; then
echo -e "\n\033[1;33mTrust the certificate in your Keychain manually to avoid errors.\033[0m\n"
exit 1
fi

echo -e "\n"

cert_name="Dev Proxy CA"
# export cert from keychain to PEM
echo "Exporting Dev Proxy certificate..."
security find-certificate -c "$cert_name" -a -p > dev-proxy-ca.pem
# add trusted cert to keychain
echo "Updating Dev Proxy trust settings..."
security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db dev-proxy-ca.pem
# remove exported cert
echo "Cleaning up..."
rm dev-proxy-ca.pem
echo -e "\033[0;32mDONE\033[0m\n"

0 comments on commit 20107fe

Please sign in to comment.