Skip to content

Commit acbe593

Browse files
Add domain name and wildcard support to no-proxy list
IP/CIDR entries continue to bypass the proxy through iptables rules. Domain and wildcard entries now generate moproxy policy rules that use "dst domain ... direct" for domain-based proxy bypass. The backend splits noproxy entries by type: IP/CIDR entries go to MOPROXY_NOPROXY for iptables processing, while domain entries produce /etc/moproxy/policy.rules consumed by the new --policy flag. SIGHUP already reloads both the proxy list and the policy file. The settings validator now accepts domain names (example.com) and wildcard domains (*.example.com) in addition to IP addresses and CIDR subnets. Ref #9803
1 parent 9448567 commit acbe593

File tree

4 files changed

+80
-13
lines changed

4 files changed

+80
-13
lines changed

pkg/rancher-desktop/assets/scripts/moproxy.initd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ description_reload="Reload the proxy list."
2525
: ${noproxy_rules:=${MOPROXY_NOPROXY:-"0.0.0.0/8,10.0.0.0/8,127.0.0.0/8,169.254.0.0/16,172.16.0.0/12,192.168.0.0/16,224.0.0.0/4,240.0.0.0/4"}}
2626

2727
command="'${MOPROXY_BINARY:-/usr/sbin/moproxy}'"
28-
command_args="--host ${host} --port ${port} ${moproxy_remotedns} --list ${proxy_list} ${moproxy_args}"
28+
command_args="--host ${host} --port ${port} ${moproxy_remotedns} --list ${proxy_list} --policy /etc/moproxy/policy.rules ${moproxy_args}"
2929
command_background="yes"
3030
pidfile="/run/${name}.pid"
3131

pkg/rancher-desktop/backend/wsl.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import events from 'events';
44
import fs from 'fs';
5+
import net from 'net';
56
import os from 'os';
67
import path from 'path';
78
import stream from 'stream';
@@ -776,7 +777,22 @@ export default class WSLBackend extends events.EventEmitter implements VMBackend
776777
await this.writeFile(`/etc/moproxy/proxy.ini`, '; no proxy defined');
777778
}
778779

779-
await this.modifyConf('moproxy', { MOPROXY_NOPROXY: proxy.noproxy.join(',') });
780+
const ipEntries: string[] = [];
781+
const domainRules: string[] = [];
782+
783+
for (const entry of proxy.noproxy) {
784+
if (net.isIP(entry) !== 0 || entry.includes('/')) {
785+
ipEntries.push(entry);
786+
} else {
787+
const domain = entry.startsWith('*.') ? entry.substring(2) : entry;
788+
789+
domainRules.push(`dst domain ${ domain } direct`);
790+
}
791+
}
792+
793+
await this.writeFile('/etc/moproxy/policy.rules',
794+
domainRules.length > 0 ? `${ domainRules.join('\n') }\n` : '; no domain noproxy rules\n');
795+
await this.modifyConf('moproxy', { MOPROXY_NOPROXY: ipEntries.join(',') });
780796
}
781797

782798
/**

pkg/rancher-desktop/main/commandServer/__tests__/settingsValidator.spec.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,22 +1035,52 @@ describe('SettingsValidator', () => {
10351035
expect({ needToUpdate, errors }).toEqual({ needToUpdate: false, errors: [] });
10361036
});
10371037

1038-
it('should reject hostnames', () => {
1038+
it('should accept domain names', () => {
10391039
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['example.com']));
10401040

1041+
expect({ needToUpdate, errors }).toEqual({ needToUpdate: true, errors: [] });
1042+
});
1043+
1044+
it('should accept wildcard domain entries', () => {
1045+
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['*.example.com']));
1046+
1047+
expect({ needToUpdate, errors }).toEqual({ needToUpdate: true, errors: [] });
1048+
});
1049+
1050+
it('should accept domain names with subdomains', () => {
1051+
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['sub.example.com']));
1052+
1053+
expect({ needToUpdate, errors }).toEqual({ needToUpdate: true, errors: [] });
1054+
});
1055+
1056+
it('should accept mixed IP and domain entries', () => {
1057+
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['10.0.0.0/8', 'example.com', '*.internal.corp']));
1058+
1059+
expect({ needToUpdate, errors }).toEqual({ needToUpdate: true, errors: [] });
1060+
});
1061+
1062+
it('should reject single-label hostnames', () => {
1063+
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['localhost']));
1064+
10411065
expect(needToUpdate).toBe(false);
10421066
expect(errors).toHaveLength(1);
10431067
expect(errors[0]).toContain('invalid entries');
1044-
expect(errors[0]).toContain('example.com');
10451068
});
10461069

1047-
it('should reject wildcard DNS entries', () => {
1048-
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['*.example.com']));
1070+
it('should reject bare wildcard', () => {
1071+
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['*.']));
1072+
1073+
expect(needToUpdate).toBe(false);
1074+
expect(errors).toHaveLength(1);
1075+
expect(errors[0]).toContain('invalid entries');
1076+
});
1077+
1078+
it('should reject domains with labels starting with hyphens', () => {
1079+
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['-example.com']));
10491080

10501081
expect(needToUpdate).toBe(false);
10511082
expect(errors).toHaveLength(1);
10521083
expect(errors[0]).toContain('invalid entries');
1053-
expect(errors[0]).toContain('*.example.com');
10541084
});
10551085

10561086
it('should reject duplicate entries', () => {
@@ -1085,12 +1115,12 @@ describe('SettingsValidator', () => {
10851115
});
10861116

10871117
it('should report all invalid entries at once', () => {
1088-
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['foo.bar', '10.0.0.0/8', 'baz.qux']));
1118+
const [needToUpdate, errors] = subject.validateSettings(cfg, noproxySetting(['-invalid', '10.0.0.0/8', 'also-invalid-']));
10891119

10901120
expect(needToUpdate).toBe(false);
10911121
expect(errors).toHaveLength(1);
1092-
expect(errors[0]).toContain('foo.bar');
1093-
expect(errors[0]).toContain('baz.qux');
1122+
expect(errors[0]).toContain('-invalid');
1123+
expect(errors[0]).toContain('also-invalid-');
10941124
});
10951125

10961126
it('should be gated to win32 platform', () => {

pkg/rancher-desktop/main/commandServer/settingsValidator.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,28 @@ export default class SettingsValidator {
691691
}
692692

693693
/**
694-
* Validate the noproxy list: must be unique strings, each a valid IP or CIDR.
694+
* Validate that a string is a domain name, optionally with a wildcard prefix.
695+
* Accepts entries like "example.com" or "*.example.com".
696+
*/
697+
protected static isDomainName(entry: string): boolean {
698+
const domain = entry.startsWith('*.') ? entry.substring(2) : entry;
699+
700+
if (domain.length === 0 || domain.length > 253) {
701+
return false;
702+
}
703+
const labels = domain.split('.');
704+
705+
if (labels.length < 2) {
706+
return false;
707+
}
708+
const labelRE = /^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
709+
710+
return labels.every(label => labelRE.test(label));
711+
}
712+
713+
/**
714+
* Validate the noproxy list: must be unique strings, each a valid IP address,
715+
* CIDR subnet, domain name, or wildcard domain.
695716
*/
696717
protected checkNoproxyList<S>(mergedSettings: S, currentValue: string[], desiredValue: string[], errors: string[], fqname: string): boolean {
697718
if (!Array.isArray(desiredValue) || desiredValue.some(s => typeof (s) !== 'string')) {
@@ -707,10 +728,10 @@ export default class SettingsValidator {
707728

708729
return false;
709730
}
710-
const invalidEntries = desiredValue.filter(entry => !SettingsValidator.isIPAddressOrCIDR(entry));
731+
const invalidEntries = desiredValue.filter(entry => !SettingsValidator.isIPAddressOrCIDR(entry) && !SettingsValidator.isDomainName(entry));
711732

712733
if (invalidEntries.length > 0) {
713-
errors.push(`field "${ fqname }" has invalid entries (must be IP addresses or CIDR subnets): "${ invalidEntries.join('", "') }"`);
734+
errors.push(`field "${ fqname }" has invalid entries (must be IP addresses, CIDR subnets, or domain names): "${ invalidEntries.join('", "') }"`);
714735

715736
return false;
716737
}

0 commit comments

Comments
 (0)