Skip to content

Commit f12b3cb

Browse files
Adds cert setup for macOS. Closes #49 (#431)
Co-authored-by: Garry Trinder <[email protected]>
1 parent cade5ad commit f12b3cb

File tree

6 files changed

+84
-2
lines changed

6 files changed

+84
-2
lines changed

dev-proxy/ProxyCommandHandler.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class ProxyCommandHandler : ICommandHandler {
1616
public Option<IEnumerable<int>?> WatchPids { get; set; }
1717
public Option<IEnumerable<string>?> WatchProcessNames { get; set; }
1818
public Option<int?> Rate { get; set; }
19+
public Option<bool?> NoFirstRun { get; set; }
1920

2021
private readonly PluginEvents _pluginEvents;
2122
private readonly ISet<UrlToWatch> _urlsToWatch;
@@ -28,6 +29,7 @@ public ProxyCommandHandler(Option<int?> port,
2829
Option<IEnumerable<int>?> watchPids,
2930
Option<IEnumerable<string>?> watchProcessNames,
3031
Option<int?> rate,
32+
Option<bool?> noFirstRun,
3133
PluginEvents pluginEvents,
3234
ISet<UrlToWatch> urlsToWatch,
3335
ILogger logger) {
@@ -38,6 +40,7 @@ public ProxyCommandHandler(Option<int?> port,
3840
WatchPids = watchPids ?? throw new ArgumentNullException(nameof(watchPids));
3941
WatchProcessNames = watchProcessNames ?? throw new ArgumentNullException(nameof(watchProcessNames));
4042
Rate = rate ?? throw new ArgumentNullException(nameof(rate));
43+
NoFirstRun = noFirstRun ?? throw new ArgumentNullException(nameof(noFirstRun));
4144
_pluginEvents = pluginEvents ?? throw new ArgumentNullException(nameof(pluginEvents));
4245
_urlsToWatch = urlsToWatch ?? throw new ArgumentNullException(nameof(urlsToWatch));
4346
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -76,6 +79,10 @@ public async Task<int> InvokeAsync(InvocationContext context) {
7679
if (rate is not null) {
7780
Configuration.Rate = rate.Value;
7881
}
82+
var noFirstRun = context.ParseResult.GetValueForOption(NoFirstRun);
83+
if (noFirstRun is not null) {
84+
Configuration.NoFirstRun = noFirstRun.Value;
85+
}
7986

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

dev-proxy/ProxyConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class ProxyConfiguration: IProxyConfiguration {
2323
public IEnumerable<string> WatchProcessNames { get; set; } = new List<string>();
2424
[JsonPropertyName("rate")]
2525
public int Rate { get; set; } = 50;
26+
public bool NoFirstRun { get; set; } = false;
2627
public string ConfigFile { get; set; } = "devproxyrc.json";
2728
}
2829

dev-proxy/ProxyEngine.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public async Task Run(CancellationToken? cancellationToken) {
8484

8585
_proxyServer = new ProxyServer();
8686

87+
_proxyServer.CertificateManager.RootCertificateName = "Dev Proxy CA";
8788
_proxyServer.CertificateManager.CertificateStorage = new CertificateDiskCache();
8889
_proxyServer.BeforeRequest += OnRequest;
8990
_proxyServer.BeforeResponse += OnBeforeResponse;
@@ -109,6 +110,9 @@ public async Task Run(CancellationToken? cancellationToken) {
109110
_proxyServer.AddEndPoint(_explicitEndPoint);
110111
_proxyServer.Start();
111112

113+
// run first-run setup on macOS
114+
FirstRunSetup();
115+
112116
foreach (var endPoint in _proxyServer.ProxyEndPoints) {
113117
_logger.LogInfo($"Listening on {endPoint.IpAddress}:{endPoint.Port}...");
114118
}
@@ -143,6 +147,46 @@ public async Task Run(CancellationToken? cancellationToken) {
143147
while (_proxyServer.ProxyRunning) { await Task.Delay(10); }
144148
}
145149

150+
private void FirstRunSetup()
151+
{
152+
if (!RunTime.IsMac ||
153+
_config.NoFirstRun ||
154+
!IsFirstRun())
155+
{
156+
return;
157+
}
158+
159+
var bashScriptPath = Path.Join(ProxyUtils.AppFolder, "trust-cert.sh");
160+
ProcessStartInfo startInfo = new ProcessStartInfo()
161+
{
162+
FileName = "/bin/bash",
163+
Arguments = bashScriptPath,
164+
UseShellExecute = true,
165+
CreateNoWindow = false
166+
};
167+
168+
var process = new Process() { StartInfo = startInfo };
169+
process.Start();
170+
process.WaitForExit();
171+
}
172+
173+
private bool IsFirstRun()
174+
{
175+
var firstRunFilePath = Path.Combine(ProxyUtils.AppFolder!, ".hasrun");
176+
if (File.Exists(firstRunFilePath))
177+
{
178+
return false;
179+
}
180+
181+
try
182+
{
183+
File.WriteAllText(firstRunFilePath, "");
184+
}
185+
catch {}
186+
187+
return true;
188+
}
189+
146190
private void AfterRequestLog(object? sender, RequestLogArgs e) {
147191
if (!_isRecording)
148192
{

dev-proxy/ProxyHost.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ internal class ProxyHost
1717
private Option<IEnumerable<string>?> _watchProcessNamesOption;
1818
private static Option<string?>? _configFileOption;
1919
private Option<int?> _rateOption;
20+
private Option<bool?> _noFirstRunOption;
2021

2122
private static bool _configFileResolved = false;
2223
private static string _configFile = "devproxyrc.json";
@@ -175,6 +176,8 @@ public ProxyHost() {
175176
}
176177
});
177178

179+
_noFirstRunOption = new Option<bool?>("--no-first-run", "Skip the first run experience");
180+
178181
ProxyCommandHandler.Configuration.ConfigFile = ConfigFile;
179182
}
180183

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

@@ -205,6 +209,6 @@ public RootCommand GetRootCommand(ILogger logger)
205209
return command;
206210
}
207211

208-
public ProxyCommandHandler GetCommandHandler(PluginEvents pluginEvents, ISet<UrlToWatch> urlsToWatch, ILogger logger) => new ProxyCommandHandler(_portOption, _ipAddressOption, _logLevelOption!, _recordOption, _watchPidsOption, _watchProcessNamesOption, _rateOption, pluginEvents, urlsToWatch, logger);
212+
public ProxyCommandHandler GetCommandHandler(PluginEvents pluginEvents, ISet<UrlToWatch> urlsToWatch, ILogger logger) => new ProxyCommandHandler(_portOption, _ipAddressOption, _logLevelOption, _recordOption, _watchPidsOption, _watchProcessNamesOption, _rateOption, _noFirstRunOption, pluginEvents, urlsToWatch, logger);
209213
}
210214

dev-proxy/dev-proxy.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
<None Update="responses.json">
6060
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
6161
</None>
62+
<None Update="trust-cert.sh">
63+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
64+
</None>
6265
<None Update="responses.sample.json">
6366
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
6467
</None>

dev-proxy/trust-cert.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
set -e
2+
3+
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"
4+
read -n 1 answer
5+
6+
if [ "$answer" = "n" ]; then
7+
echo -e "\n\033[1;33mTrust the certificate in your Keychain manually to avoid errors.\033[0m\n"
8+
exit 1
9+
fi
10+
11+
echo -e "\n"
12+
13+
cert_name="Dev Proxy CA"
14+
# export cert from keychain to PEM
15+
echo "Exporting Dev Proxy certificate..."
16+
security find-certificate -c "$cert_name" -a -p > dev-proxy-ca.pem
17+
# add trusted cert to keychain
18+
echo "Updating Dev Proxy trust settings..."
19+
security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db dev-proxy-ca.pem
20+
# remove exported cert
21+
echo "Cleaning up..."
22+
rm dev-proxy-ca.pem
23+
echo -e "\033[0;32mDONE\033[0m\n"

0 commit comments

Comments
 (0)