Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds cert setup for macOS. Closes #49 #431

Merged
merged 5 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -84,6 +84,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 @@ -109,6 +110,9 @@ public async Task Run(CancellationToken? cancellationToken) {
_proxyServer.AddEndPoint(_explicitEndPoint);
_proxyServer.Start();

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

foreach (var endPoint in _proxyServer.ProxyEndPoints) {
_logger.LogInfo($"Listening on {endPoint.IpAddress}:{endPoint.Port}...");
}
Expand Down Expand Up @@ -143,6 +147,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 @@
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 @@
}
});

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

ProxyCommandHandler.Configuration.ConfigFile = ConfigFile;
}

Expand All @@ -192,7 +195,8 @@
_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 @@
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 / 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)'.

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)'.
}

3 changes: 3 additions & 0 deletions dev-proxy/dev-proxy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,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"
garrytrinder marked this conversation as resolved.
Show resolved Hide resolved
# export cert from keychain to PEM
echo "Exporting Dev Proxy certificate..."
security find-certificate -c "$cert_name" -a -p > dev-proxy-ca.pem
garrytrinder marked this conversation as resolved.
Show resolved Hide resolved
# 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
garrytrinder marked this conversation as resolved.
Show resolved Hide resolved
# remove exported cert
echo "Cleaning up..."
rm dev-proxy-ca.pem
echo -e "\033[0;32mDONE\033[0m\n"