Allow binding to specific interface addresses

https://trello.com/c/aWazKGQc/404-allow-binding-to-specified-ip-address
This commit is contained in:
Mike 2014-10-01 14:34:10 -04:00 committed by Mark McDowall
parent 65c1e8852a
commit 5a8ce04bd5
6 changed files with 63 additions and 13 deletions

View File

@ -1,4 +1,5 @@
using System.Linq; using System;
using System.Linq;
using System.Reflection; using System.Reflection;
using FluentValidation; using FluentValidation;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -25,7 +26,9 @@ namespace NzbDrone.Api.Config
SharedValidator.RuleFor(c => c.Branch).NotEmpty().WithMessage("Branch name is required, 'master' is the default"); SharedValidator.RuleFor(c => c.Branch).NotEmpty().WithMessage("Branch name is required, 'master' is the default");
SharedValidator.RuleFor(c => c.Port).ValidPort(); SharedValidator.RuleFor(c => c.Port).ValidPort();
SharedValidator.RuleFor(c => c.BindAddress).ValidIp4Address().When(c => c.BindAddress != "*");
SharedValidator.RuleFor(c => c.Port).InclusiveBetween(1, 65535);
SharedValidator.RuleFor(c => c.Username).NotEmpty().When(c => c.AuthenticationEnabled); SharedValidator.RuleFor(c => c.Username).NotEmpty().When(c => c.AuthenticationEnabled);
SharedValidator.RuleFor(c => c.Password).NotEmpty().When(c => c.AuthenticationEnabled); SharedValidator.RuleFor(c => c.Password).NotEmpty().When(c => c.AuthenticationEnabled);

View File

@ -6,6 +6,7 @@ namespace NzbDrone.Api.Config
{ {
public class HostConfigResource : RestResource public class HostConfigResource : RestResource
{ {
public String BindAddress { get; set; }
public Int32 Port { get; set; } public Int32 Port { get; set; }
public Int32 SslPort { get; set; } public Int32 SslPort { get; set; }
public Boolean EnableSsl { get; set; } public Boolean EnableSsl { get; set; }

View File

@ -23,6 +23,7 @@ namespace NzbDrone.Core.Configuration
Dictionary<string, object> GetConfigDictionary(); Dictionary<string, object> GetConfigDictionary();
void SaveConfigDictionary(Dictionary<string, object> configValues); void SaveConfigDictionary(Dictionary<string, object> configValues);
string BindAddress { get; }
int Port { get; } int Port { get; }
int SslPort { get; } int SslPort { get; }
bool EnableSsl { get; } bool EnableSsl { get; }
@ -110,6 +111,22 @@ namespace NzbDrone.Core.Configuration
_eventAggregator.PublishEvent(new ConfigFileSavedEvent()); _eventAggregator.PublishEvent(new ConfigFileSavedEvent());
} }
public string BindAddress
{
get
{
const string defaultValue = "*";
string bindAddress = GetValue("BindAddress", defaultValue);
if (string.IsNullOrWhiteSpace(bindAddress))
{
return defaultValue;
}
return bindAddress;
}
}
public int Port public int Port
{ {
get { return GetValueInt("Port", 8989); } get { return GetValueInt("Port", 8989); }

View File

@ -1,4 +1,6 @@
using System.Text.RegularExpressions; using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using FluentValidation; using FluentValidation;
using FluentValidation.Validators; using FluentValidation.Validators;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
@ -33,6 +35,21 @@ namespace NzbDrone.Core.Validation
return ruleBuilder.SetValidator(new InclusiveBetweenValidator(1, 65535)); return ruleBuilder.SetValidator(new InclusiveBetweenValidator(1, 65535));
} }
public static IRuleBuilderOptions<T, string> ValidIp4Address<T>(this IRuleBuilder<T, string> ruleBuilder)
{
return ruleBuilder.Must(x =>
{
IPAddress parsedAddress;
if (!IPAddress.TryParse(x, out parsedAddress))
{
return false;
}
return parsedAddress.AddressFamily == AddressFamily.InterNetwork;
});
}
public static IRuleBuilderOptions<T, Language> ValidLanguage<T>(this IRuleBuilder<T, Language> ruleBuilder) public static IRuleBuilderOptions<T, Language> ValidLanguage<T>(this IRuleBuilder<T, Language> ruleBuilder)
{ {
return ruleBuilder.SetValidator(new LangaugeValidator()); return ruleBuilder.SetValidator(new LangaugeValidator());

View File

@ -37,30 +37,30 @@ namespace NzbDrone.Host.AccessControl
public void ConfigureUrl() public void ConfigureUrl()
{ {
var localHttpUrls = BuildUrls("http", "localhost", _configFileProvider.Port); var localHostHttpUrls = BuildUrls("http", "localhost", _configFileProvider.Port);
var wildcardHttpUrls = BuildUrls("http", "*", _configFileProvider.Port); var interfaceHttpUrls = BuildUrls("http", _configFileProvider.BindAddress, _configFileProvider.Port);
var localHttpsUrls = BuildUrls("https", "localhost", _configFileProvider.SslPort); var localHostHttpsUrls = BuildUrls("https", "localhost", _configFileProvider.SslPort);
var wildcardHttpsUrls = BuildUrls("https", "*", _configFileProvider.SslPort); var interfaceHttpsUrls = BuildUrls("https", _configFileProvider.BindAddress, _configFileProvider.SslPort);
if (!_configFileProvider.EnableSsl) if (!_configFileProvider.EnableSsl)
{ {
localHttpsUrls.Clear(); Urls.Clear();
wildcardHttpsUrls.Clear(); interfaceHttpsUrls.Clear();
} }
if (OsInfo.IsWindows && !_runtimeInfo.IsAdmin) if (OsInfo.IsWindows && !_runtimeInfo.IsAdmin)
{ {
var httpUrls = wildcardHttpUrls.All(IsRegistered) ? wildcardHttpUrls : localHttpUrls; var httpUrls = interfaceHttpUrls.All(IsRegistered) ? interfaceHttpUrls : localHostHttpUrls;
var httpsUrls = wildcardHttpsUrls.All(IsRegistered) ? wildcardHttpsUrls : localHttpsUrls; var httpsUrls = interfaceHttpsUrls.All(IsRegistered) ? interfaceHttpsUrls : localHostHttpsUrls;
Urls.AddRange(httpUrls); Urls.AddRange(httpUrls);
Urls.AddRange(httpsUrls); Urls.AddRange(httpsUrls);
} }
else else
{ {
Urls.AddRange(wildcardHttpUrls); Urls.AddRange(interfaceHttpUrls);
Urls.AddRange(wildcardHttpsUrls); Urls.AddRange(interfaceHttpsUrls);
if (OsInfo.IsWindows) if (OsInfo.IsWindows)
{ {

View File

@ -2,6 +2,18 @@
<fieldset> <fieldset>
<legend>Start-Up</legend> <legend>Start-Up</legend>
<div class="form-group advanced-setting">
<label class="col-sm-3 control-label">Bind Address</label>
<div class="col-sm-1 col-sm-push-4 help-inline">
<i class="icon-nd-form-warning" title="Requires restart to take effect" />
</div>
<div class="col-sm-4 col-sm-pull-1">
<input type="text" placeholder="*" name="bindAddress" class="form-control" />
</div>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label">Port Number</label> <label class="col-sm-3 control-label">Port Number</label>