Skip to content

Commit 95e15d2

Browse files
Cleanup FQDN validation logic, fallback to old hostname check (pterodactyl#4409)
Co-authored-by: DaneEveritt <dane@daneeveritt.com>
1 parent c748fa9 commit 95e15d2

File tree

3 files changed

+87
-49
lines changed

3 files changed

+87
-49
lines changed

app/Console/Commands/Node/MakeNodeCommand.php

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,29 +58,13 @@ public function handle(NodeCreationService $creationService)
5858
$data['name'] = $this->option('name') ?? $this->ask('Enter a short identifier used to distinguish this node from others');
5959
$data['description'] = $this->option('description') ?? $this->ask('Enter a description to identify the node');
6060
$data['location_id'] = $this->option('locationId') ?? $this->ask('Enter a valid location id');
61-
$data['fqdn'] = $this->option('fqdn') ?? $this->ask('Enter a domain name (e.g node.example.com) to be used for connecting to the daemon. An IP address may only be used if you are not using SSL for this node');
62-
63-
// Note, this function will also resolve CNAMEs for us automatically,
64-
// there is no need to manually resolve them here.
65-
//
66-
// Using @ as workaround to fix https://bugs.php.net/bug.php?id=73149
67-
$records = @dns_get_record($data['fqdn'], DNS_A + DNS_AAAA);
68-
if (empty($records)) {
69-
$this->error('The FQDN or IP address provided does not resolve to a valid IP address.');
70-
71-
return;
72-
}
73-
$data['public'] = $this->option('public') ?? $this->confirm('Should this node be public? As a note, setting a node to private you will be denying the ability to auto-deploy to this node.', true);
7461
$data['scheme'] = $this->option('scheme') ?? $this->anticipate(
7562
'Please either enter https for SSL or http for a non-ssl connection',
7663
['https', 'http'],
7764
'https'
7865
);
79-
if (filter_var($data['fqdn'], FILTER_VALIDATE_IP) && $data['scheme'] === 'https') {
80-
$this->error('A fully qualified domain name that resolves to a public IP address is required in order to use SSL for this node.');
81-
82-
return;
83-
}
66+
$data['fqdn'] = $this->option('fqdn') ?? $this->ask('Enter a domain name (e.g node.example.com) to be used for connecting to the daemon. An IP address may only be used if you are not using SSL for this node');
67+
$data['public'] = $this->option('public') ?? $this->confirm('Should this node be public? As a note, setting a node to private you will be denying the ability to auto-deploy to this node.', true);
8468
$data['behind_proxy'] = $this->option('proxy') ?? $this->confirm('Is your FQDN behind a proxy?');
8569
$data['maintenance_mode'] = $this->option('maintenance') ?? $this->confirm('Should maintenance mode be enabled?');
8670
$data['memory'] = $this->option('maxMemory') ?? $this->ask('Enter the maximum amount of memory');
Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
<?php
2-
/**
3-
* Pterodactyl - Panel
4-
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5-
*
6-
* This software is licensed under the terms of the MIT license.
7-
* https://opensource.org/licenses/MIT
8-
*/
92

103
namespace Pterodactyl\Http\Requests\Admin\Node;
114

5+
use Pterodactyl\Rules\Fqdn;
126
use Pterodactyl\Models\Node;
137
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
148

@@ -23,30 +17,9 @@ public function rules()
2317
return Node::getRulesForUpdate($this->route()->parameter('node'));
2418
}
2519

26-
return Node::getRules();
27-
}
28-
29-
/**
30-
* Run validation after the rules above have been applied.
31-
*
32-
* @param \Illuminate\Validation\Validator $validator
33-
*/
34-
public function withValidator($validator)
35-
{
36-
$validator->after(function ($validator) {
37-
// Note, this function will also resolve CNAMEs for us automatically,
38-
// there is no need to manually resolve them here.
39-
//
40-
// Using @ as workaround to fix https://bugs.php.net/bug.php?id=73149
41-
$records = @dns_get_record($this->input('fqdn'), DNS_A + DNS_AAAA);
42-
if (empty($records)) {
43-
$validator->errors()->add('fqdn', trans('admin/node.validation.fqdn_not_resolvable'));
44-
}
20+
$data = Node::getRules();
21+
$data['fqdn'][] = Fqdn::make('scheme');
4522

46-
// Check that if using HTTPS the FQDN is not an IP address.
47-
if (filter_var($this->input('fqdn'), FILTER_VALIDATE_IP) && $this->input('scheme') === 'https') {
48-
$validator->errors()->add('fqdn', trans('admin/node.validation.fqdn_required_for_ssl'));
49-
}
50-
});
23+
return $data;
5124
}
5225
}

app/Rules/Fqdn.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace Pterodactyl\Rules;
4+
5+
use Illuminate\Support\Arr;
6+
use Illuminate\Contracts\Validation\Rule;
7+
use Illuminate\Contracts\Validation\DataAwareRule;
8+
9+
class Fqdn implements Rule, DataAwareRule
10+
{
11+
protected array $data = [];
12+
protected string $message = '';
13+
protected ?string $schemeField = null;
14+
15+
/**
16+
* @param array $data
17+
*/
18+
public function setData($data): self
19+
{
20+
$this->data = $data;
21+
22+
return $this;
23+
}
24+
25+
/**
26+
* Validates that the value provided resolves to an IP address. If a scheme is
27+
* specified when this rule is created additional checks will be applied.
28+
*
29+
* @param string $attribute
30+
* @param mixed $value
31+
* @return bool
32+
*/
33+
public function passes($attribute, $value)
34+
{
35+
if (filter_var($value, FILTER_VALIDATE_IP)) {
36+
// Check if the scheme is set to HTTPS.
37+
//
38+
// Unless someone owns their IP blocks and decides to pay who knows how much for a
39+
// custom SSL cert, IPs will not be able to use HTTPS. This should prevent most
40+
// home users from making this mistake and wondering why their node is not working.
41+
if ($this->schemeField && Arr::get($this->data, $this->schemeField) === 'https') {
42+
$this->message = 'The :attribute must not be an IP address when HTTPS is enabled.';
43+
44+
return false;
45+
}
46+
47+
return true;
48+
}
49+
50+
// Lookup A and AAAA DNS records for the FQDN. Note, this function will also resolve CNAMEs
51+
// for us automatically, there is no need to manually resolve them here.
52+
//
53+
// The error suppression is intentional, see https://bugs.php.net/bug.php?id=73149
54+
$records = @dns_get_record($value, DNS_A + DNS_AAAA);
55+
// If no records were returned fall back to trying to resolve the value using the hosts DNS
56+
// resolution. This will not work for IPv6 which is why we prefer to use `dns_get_record`
57+
// first.
58+
if (!empty($records) || filter_var(gethostbyname($value), FILTER_VALIDATE_IP)) {
59+
return true;
60+
}
61+
62+
$this->message = 'The :attribute could not be resolved to a valid IP address.';
63+
64+
return false;
65+
}
66+
67+
public function message(): string
68+
{
69+
return $this->message;
70+
}
71+
72+
/**
73+
* Returns a new instance of the rule with a defined scheme set.
74+
*/
75+
public static function make(string $schemeField = null): self
76+
{
77+
return tap(new static(), function ($fqdn) use ($schemeField) {
78+
$fqdn->schemeField = $schemeField;
79+
});
80+
}
81+
}

0 commit comments

Comments
 (0)