Skip to content

Commit caf06c1

Browse files
committed
Initial implementation of authentication bypass for scanners
1 parent 8ebc706 commit caf06c1

17 files changed

Lines changed: 422 additions & 21 deletions

README.md

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,46 @@ A Custom Http Handler that implements RUTA for: https://jira.sonarsource.com/bro
44

55
[![Build status](https://ci.appveyor.com/api/projects/status/n3cgxias5t3mfybr?svg=true)](https://ci.appveyor.com/project/jabbera/iisremoteusertokenauthentication)
66

7-
# Installation
7+
# Administrivia
88

9-
SonarQube scanners DO NOT support anything other then basic\token based authentication. Because of that you will need to setup 2 websites. The first is for the browser and supports single sign on. [WWWROOT_USER] You will also need an unauthenticated one for supporting scanners [WWWROOT_SCANNER]. Please setup these websites ahead of time (ssl required) and make sure you can access index.html. DO NOT USE AN SNI based website for WWWROOT_SCANNER. Run it on a different port then 443. There is a bug that makes it unsupported.
9+
SonarQube scanners DO NOT support anything other then basic\token based authentication. I've created a module that attempts to detect when the connecting application is a scanner or includes a token. When a scanner is detected the module will then bypass the windows authentication process. Right now the bypass conditions are:
10+
* If there is an Authorization header with Basic auth
11+
* this indicates a token is present
12+
* If the user agent of the request starts with any of the agent strings listed in the web.config setting: PassThruAgents
13+
* Initial configuration: sonar.scanner.app, SonarQubeScanner
14+
15+
I've only tested this with the MsBuild scanner so the agent list may need to be expanded.
16+
17+
Previously the only way to enable single sign on was to have 2 websites, one for windows authentication, and one for token based authentication. The bypass module SHOULD remove the need for that second site. Until there is more exhaustive testing I will still include the multi-site installation instructions and artifacts. My plan is to remove those once the single site method is proven stable.
18+
19+
20+
21+
# Installation (Single Site)
22+
23+
These are the prefered installation directions.
24+
25+
`Note: This configuration assumes sonarqube is running on the same server as IIS on port 9000. If this is not correct you will need to edit the reverse proxy rules in the web.config file to match your configuration.`
26+
27+
1) Configure sonarqube for RUTA per: https://jira.sonarsource.com/browse/SONAR-5430 (If default settings are used all you should need to do is add: sonar.web.sso.enable=true to the sonar.properties file and restart sonarqube.)
28+
29+
2) Download the current release and extract to [EXTRACT_FOLDER]
30+
31+
3) Run: ConfigureServer.ps1
32+
`Note: This installs the following windows features: IIS-HttpRedirect, IIS-ASPNET45, IIS-WebServerManagementTools, IIS-HttpTracing, IIS-WindowsAuthentication, IIS-NetFxExtensibility45, IIS-ApplicationDevelopment. It unlocks the IIS module ordering system wide as well as the authentication module configuration. It also installs ARR and UrlRewrite server wide.`
33+
34+
4) Create a website [WWWROOT], ssl required, pointing to a directory [WWWROOT_DIRECTORY] with a test file in it. Make sure you can browse to that file via your browser.
35+
36+
5) Copy: [EXTRACT_FOLDER]\inetpub-user to: [WWWROOT_DIRECTORY]
37+
38+
6) Browse to https://[WWWROOT] You should hopefully be signed in.
39+
40+
7) Test a scanner run with the url https://[WWWROOT]
41+
42+
# Installation (Multi Site)
43+
44+
These directions are only if you run into trouble with the single site method.
45+
46+
The first site is for the browser and supports single sign on. [WWWROOT_USER] You will also need an unauthenticated one for supporting scanners [WWWROOT_SCANNER]. Please setup these websites ahead of time (ssl required) and make sure you can access index.html. DO NOT USE AN SNI based website for WWWROOT_SCANNER. Run it on a different port then 443. There is a bug that makes it unsupported.
1047

1148
1) Configure sonarqube for RUTA per: https://jira.sonarsource.com/browse/SONAR-5430 (If default settings are used all you should need to do is add: sonar.web.sso.enable=true to the sonar.properties file and restart sonarqube.)
1249

@@ -17,13 +54,15 @@ SonarQube scanners DO NOT support anything other then basic\token based authenti
1754

1855
4) Copy: [EXTRACT_FOLDER]\inetpub-user to: [WWWROOT_USER]
1956

20-
5) Browse to https://[WWWROOT_USER_URL] You should hopefully be signed in.
57+
5) Remove the line: <add name="SonarAuthPassthroughModule" type="RutaHttpModule.SonarAuthPassthroughModule" preCondition="runtimeVersionv4.0" /> from the web.config file.
58+
59+
6) Browse to https://[WWWROOT_USER_URL] You should hopefully be signed in.
2160

2261
`Note: This configuration assumes sonarqube is running on the same server as IIS on port 9000. If this is not correct you will need to edit the reverse proxy rules in the web.config file to match your configuration.`
2362

24-
6) Once you have the SSO working the only thing left is to configure the reverse proxy on [WWWROOT_SCANNER].
63+
7) Once you have the SSO working the only thing left is to configure the reverse proxy on [WWWROOT_SCANNER].
2564

26-
7) Copy:[EXTRACT_FOLDER]\inetpub-scanner to: [WWWROOT_SCANNER]
65+
8) Copy:[EXTRACT_FOLDER]\inetpub-scanner to: [WWWROOT_SCANNER]
2766

2867
8) You should now be able to run a scanner configured to point at: https://[WWWROOT_SCANNER_URL] with token based authentication.
2968

@@ -43,5 +82,7 @@ While this should work fine out of the box there are a few options that can be u
4382

4483
6) AdGroupBaseDsn - For users, only return groups that are in the following OU.
4584

85+
7) PassThruAgents - If you discover new user agent strings that are not bypassing windows authentication add them to this list. (Please open an issue or pull request also.)
86+
4687
Note: For large AD trees setting AdUserBaseDsn and AdGroupBaseDsn can greatly improve performance.
4788

RutaHttpModule/IRutaHttpContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
internal interface IRutaHttpContext
44
{
5+
bool IsWindowsUser { get; }
56
bool IsAuthenticated { get; }
67
string DomainUserName { get; }
78
void RemoveRequestHeader(string header);

RutaHttpModule/ISettings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ internal interface ISettings
1111
string AppendString { get; }
1212
string AdUserBaseDn { get; }
1313
string AdGroupBaseDn { get; }
14+
string[] PassThruUserAgents { get; }
1415
}
1516
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace RutaHttpModule
2+
{
3+
using System.Security.Principal;
4+
5+
internal interface ISonarAuthPassthroughHttpContext
6+
{
7+
IPrincipal User { get; set; }
8+
bool HasTokenHeader { get; }
9+
string UserAgent { get; }
10+
bool SkipAuthorization { get; set; }
11+
}
12+
}

RutaHttpModule/Properties/Settings.Designer.cs

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

RutaHttpModule/Properties/Settings.settings

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,12 @@
2929
<Setting Name="AppendString" Type="System.String" Scope="Application">
3030
<Value Profile="(Default)" />
3131
</Setting>
32+
<Setting Name="PassThruAgents" Type="System.Collections.Specialized.StringCollection" Scope="Application">
33+
<Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
34+
&lt;ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
35+
&lt;string&gt;sonar.scanner.app&lt;/string&gt;
36+
&lt;string&gt;SonarQubeScanner&lt;/string&gt;
37+
&lt;/ArrayOfString&gt;</Value>
38+
</Setting>
3239
</Settings>
3340
</SettingsFile>

RutaHttpModule/RutaHttpContext.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
using System;
2-
using System.Diagnostics.CodeAnalysis;
3-
using System.Web;
4-
5-
namespace RutaHttpModule
1+
namespace RutaHttpModule
62
{
3+
using System;
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Security.Principal;
6+
using System.Web;
7+
78
[ExcludeFromCodeCoverage]
89
internal class RutaHttpContext : IRutaHttpContext
910
{
1011
private readonly HttpContext httpContext;
1112

1213
internal RutaHttpContext(HttpContext httpContext) => this.httpContext = httpContext ?? throw new ArgumentNullException(nameof(httpContext));
1314

15+
public bool IsWindowsUser => this.httpContext.User is WindowsPrincipal;
1416
public string DomainUserName => this.httpContext.User?.Identity?.Name;
1517
public bool IsAuthenticated => this.httpContext.User?.Identity?.IsAuthenticated ?? false;
1618

RutaHttpModule/RutaHttpModule.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
<Compile Include="IAdInteraction.cs" />
5757
<Compile Include="IRutaHttpContext.cs" />
5858
<Compile Include="ISettings.cs" />
59+
<Compile Include="ISonarAuthPassthroughHttpContext.cs" />
5960
<Compile Include="ITraceSource.cs" />
6061
<Compile Include="LdapExtensions.cs" />
6162
<Compile Include="Properties\Settings.Designer.cs">
@@ -68,6 +69,8 @@
6869
<Compile Include="Properties\AssemblyInfo.cs" />
6970
<Compile Include="RutaTraceSource.cs" />
7071
<Compile Include="SettingsWrapper.cs" />
72+
<Compile Include="SonarAuthPassthroughModule.cs" />
73+
<Compile Include="SonarAuthPassthroughHttpContext.cs" />
7174
</ItemGroup>
7275
<ItemGroup>
7376
<None Include="app.config" />

RutaHttpModule/RutaModule.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public sealed class RutaModule : IHttpModule
2828
/// <summary>
2929
/// Reference to the tracing object.
3030
/// </summary>
31-
private ITraceSource traceSource;
31+
private readonly ITraceSource traceSource;
3232

3333
/// <summary>
3434
/// Initializes a new instance of the a <see cref="RutaModule"/> object
@@ -49,13 +49,13 @@ internal RutaModule(IAdInteraction adInteraction, ISettings settings, ITraceSour
4949
{
5050
this.adInteraction = adInteraction ?? throw new ArgumentNullException(nameof(adInteraction));
5151
this.settings = settings ?? throw new ArgumentNullException(nameof(settings));
52-
this.traceSource = traceSource ?? throw new ArgumentException(nameof(traceSource));
52+
this.traceSource = traceSource ?? throw new ArgumentNullException(nameof(traceSource));
5353
}
5454

5555
/// <summary>
5656
/// The name of the module
5757
/// </summary>
58-
public string ModuleName => "RutaModule";
58+
public string ModuleName => nameof(RutaModule);
5959

6060
/// <summary>
6161
/// Initializes a module and prepares it to handle requests (part of the <see cref="IHttpHandler"/> interface).
@@ -123,6 +123,12 @@ private void HandleAuthorizeRequestInternal(IRutaHttpContext context)
123123
return;
124124
}
125125

126+
if (!context.IsWindowsUser)
127+
{
128+
traceSource.TraceEvent(TraceEventType.Information, 0, "Not a windows user");
129+
return;
130+
}
131+
126132
string userName = context.DomainUserName;
127133
if (string.IsNullOrWhiteSpace(userName))
128134
{

RutaHttpModule/SettingsWrapper.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
using System.Diagnostics.CodeAnalysis;
2-
3-
namespace RutaHttpModule
1+
namespace RutaHttpModule
42
{
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Linq;
5+
56
[ExcludeFromCodeCoverage]
67
internal class SettingsWrapper : ISettings
78
{
9+
private readonly string[] passThruUserAgents = Properties.Settings.Default.PassThruAgents.Cast<string>().ToArray();
10+
11+
812
public string AdGroupBaseDn => Properties.Settings.Default.AdGroupBaseDn;
913
public string AdUserBaseDn => Properties.Settings.Default.AdUserBaseDn;
1014
public bool DowncaseUsers => Properties.Settings.Default.DowncaseUsers;
@@ -14,5 +18,6 @@ internal class SettingsWrapper : ISettings
1418
public string LoginHeader => Properties.Settings.Default.LoginHeader;
1519
public string NameHeader => Properties.Settings.Default.NameHeader;
1620
public string AppendString => Properties.Settings.Default.AppendString;
21+
public string[] PassThruUserAgents => passThruUserAgents;
1722
}
1823
}

0 commit comments

Comments
 (0)