feat(connect): add -SkipCertificateCheck and -AllowInsecureTransport switches #11
@@ -84,5 +84,38 @@ namespace PSInfisicalAPI.Tests
|
||||
Assert.Equal("explicit-org", cmdlet.CallResolveOrganizationId(ConnectionWithDefaults(), "explicit-org"));
|
||||
Assert.Empty(logger.VerboseEntries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InfisicalConnection_Defaults_TransportFlags_To_False()
|
||||
{
|
||||
InfisicalConnection connection = new InfisicalConnection();
|
||||
Assert.False(connection.SkipCertificateCheck);
|
||||
Assert.False(connection.AllowInsecureTransport);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldSkipCertificateCheck_Reads_From_Current_Session()
|
||||
{
|
||||
InfisicalConnection previous = InfisicalSessionManager.Current;
|
||||
try
|
||||
{
|
||||
TestCmdlet cmdlet = CreateCmdletWith(new RecordingLogger());
|
||||
MethodInfo virt = typeof(InfisicalCmdletBase).GetMethod("ShouldSkipCertificateCheck", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
InfisicalSessionManager.SetCurrent(null);
|
||||
Assert.False((bool)virt.Invoke(cmdlet, null));
|
||||
|
||||
InfisicalConnection session = ConnectionWithDefaults();
|
||||
session.IsConnected = true;
|
||||
session.SkipCertificateCheck = true;
|
||||
InfisicalSessionManager.SetCurrent(session);
|
||||
|
||||
Assert.True((bool)virt.Invoke(cmdlet, null));
|
||||
}
|
||||
finally
|
||||
{
|
||||
InfisicalSessionManager.SetCurrent(previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,12 +62,24 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
[Parameter]
|
||||
public SwitchParameter PassThru { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public SwitchParameter SkipCertificateCheck { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public SwitchParameter AllowInsecureTransport { get; set; }
|
||||
|
||||
protected override bool ShouldSkipCertificateCheck()
|
||||
{
|
||||
return SkipCertificateCheck.IsPresent;
|
||||
}
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
try
|
||||
{
|
||||
ResolveMissingParametersFromEnvironment();
|
||||
ValidateRequiredParameters();
|
||||
ValidateTransportSafety();
|
||||
|
||||
IInfisicalAuthProvider provider;
|
||||
InfisicalAuthenticationRequest request;
|
||||
@@ -179,7 +191,9 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
ConnectedAtUtc = DateTimeOffset.UtcNow,
|
||||
ExpiresAtUtc = authResult.ExpiresAtUtc,
|
||||
IsConnected = true,
|
||||
AccessToken = authResult.AccessToken
|
||||
AccessToken = authResult.AccessToken,
|
||||
SkipCertificateCheck = SkipCertificateCheck.IsPresent,
|
||||
AllowInsecureTransport = AllowInsecureTransport.IsPresent
|
||||
};
|
||||
|
||||
InfisicalSessionManager.SetCurrent(connection);
|
||||
@@ -195,6 +209,26 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateTransportSafety()
|
||||
{
|
||||
bool isHttp = BaseUri != null && string.Equals(BaseUri.Scheme, "http", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (isHttp && !AllowInsecureTransport.IsPresent)
|
||||
{
|
||||
throw new InfisicalConfigurationException("BaseUri '" + BaseUri + "' is not HTTPS. Re-run Connect-Infisical with -AllowInsecureTransport to permit plaintext.");
|
||||
}
|
||||
|
||||
if (SkipCertificateCheck.IsPresent)
|
||||
{
|
||||
Logger.Warning(Component, "SkipCertificateCheck is enabled. TLS certificate validation is disabled for this session. Do not use in production.");
|
||||
}
|
||||
|
||||
if (AllowInsecureTransport.IsPresent && isHttp)
|
||||
{
|
||||
Logger.Warning(Component, "AllowInsecureTransport is enabled and BaseUri uses HTTP. Credentials and secrets will traverse the network unencrypted. Do not use in production.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveMissingParametersFromEnvironment()
|
||||
{
|
||||
bool tokenSet = string.Equals(ParameterSetName, ParameterSetToken, StringComparison.Ordinal);
|
||||
|
||||
@@ -31,13 +31,19 @@ namespace PSInfisicalAPI.Cmdlets
|
||||
{
|
||||
if (_httpClient == null)
|
||||
{
|
||||
_httpClient = new InfisicalHttpClient(Logger);
|
||||
_httpClient = new InfisicalHttpClient(Logger, 100, ShouldSkipCertificateCheck());
|
||||
}
|
||||
|
||||
return _httpClient;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool ShouldSkipCertificateCheck()
|
||||
{
|
||||
InfisicalConnection current = InfisicalSessionManager.Current;
|
||||
return current != null && current.SkipCertificateCheck;
|
||||
}
|
||||
|
||||
protected void ThrowTerminatingForException(string component, string operation, Exception exception)
|
||||
{
|
||||
InfisicalErrorDetails details = InfisicalErrorHandler.BuildDetails(component, operation, exception);
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace PSInfisicalAPI.Connections
|
||||
public DateTimeOffset ConnectedAtUtc { get; set; }
|
||||
public DateTimeOffset? ExpiresAtUtc { get; set; }
|
||||
public bool IsConnected { get; set; }
|
||||
public bool SkipCertificateCheck { get; set; }
|
||||
public bool AllowInsecureTransport { get; set; }
|
||||
|
||||
public Dictionary<string, string> ResolvedEndpointVersions { get; } = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using PSInfisicalAPI.Errors;
|
||||
using PSInfisicalAPI.Logging;
|
||||
@@ -11,13 +13,18 @@ namespace PSInfisicalAPI.Http
|
||||
public sealed class InfisicalHttpClient : IInfisicalHttpClient
|
||||
{
|
||||
private const string Component = "HttpClient";
|
||||
private static readonly PropertyInfo PerRequestCertCallbackProperty =
|
||||
typeof(HttpWebRequest).GetProperty("ServerCertificateValidationCallback");
|
||||
|
||||
private readonly IInfisicalLogger _logger;
|
||||
private readonly int _timeoutSeconds;
|
||||
private readonly bool _skipCertificateCheck;
|
||||
|
||||
public InfisicalHttpClient(IInfisicalLogger logger, int timeoutSeconds = 100)
|
||||
public InfisicalHttpClient(IInfisicalLogger logger, int timeoutSeconds = 100, bool skipCertificateCheck = false)
|
||||
{
|
||||
_logger = logger ?? NullInfisicalLogger.Instance;
|
||||
_timeoutSeconds = timeoutSeconds;
|
||||
_skipCertificateCheck = skipCertificateCheck;
|
||||
}
|
||||
|
||||
public InfisicalHttpResponse Send(InfisicalHttpRequest request)
|
||||
@@ -44,6 +51,11 @@ namespace PSInfisicalAPI.Http
|
||||
webRequest.ReadWriteTimeout = _timeoutSeconds * 1000;
|
||||
webRequest.UseDefaultCredentials = true;
|
||||
|
||||
if (_skipCertificateCheck)
|
||||
{
|
||||
ApplyInsecureCertificateBypass(webRequest);
|
||||
}
|
||||
|
||||
IWebProxy systemProxy = WebRequest.GetSystemWebProxy();
|
||||
if (systemProxy != null)
|
||||
{
|
||||
@@ -95,6 +107,20 @@ namespace PSInfisicalAPI.Http
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyInsecureCertificateBypass(HttpWebRequest webRequest)
|
||||
{
|
||||
RemoteCertificateValidationCallback callback = (sender, certificate, chain, errors) => true;
|
||||
|
||||
if (PerRequestCertCallbackProperty != null && PerRequestCertCallbackProperty.CanWrite)
|
||||
{
|
||||
PerRequestCertCallbackProperty.SetValue(webRequest, callback, null);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Warning(Component, "Per-request ServerCertificateValidationCallback unavailable on this runtime; falling back to global ServicePointManager override for this process.");
|
||||
ServicePointManager.ServerCertificateValidationCallback = callback;
|
||||
}
|
||||
|
||||
private static void ApplyHeaders(HttpWebRequest webRequest, IDictionary<string, string> headers)
|
||||
{
|
||||
if (headers == null)
|
||||
|
||||
Reference in New Issue
Block a user