Skip to content

Commit 47bdc44

Browse files
committed
Implement --tunnel-everything & do some cleanups
Fixes #1. Also, add --force-socat, useful for testing the especially-tricky parts of MacOS support under Linux. Also cleanup the use of pseudo-configuration-file globals in a bunch of function calls; convert them to arguments instead. Also, replenerate hosts only once; don't smear repleneration all over the codebase.
1 parent 713d4fd commit 47bdc44

1 file changed

Lines changed: 42 additions & 21 deletions

File tree

tunnelencabulator.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ def replenerate_hostnames(hosts):
9595
return [replenerate_hostname(h) for h in hosts]
9696

9797

98-
def prefabulate_tunnel():
98+
def prefabulate_tunnel(tunnel_hosts, *, tunnel_net):
9999
"""
100100
Constructs a malleable logarithmic casing in such a way that there is a
101-
shared mapping of TUNNEL_HOSTS to pre-fabulated IP addresses.
101+
shared mapping of tunnel_hosts to pre-fabulated IP addresses.
102102
"""
103-
return {host: f"{TUNNEL_NET}{ip}" for (ip, host)
104-
in enumerate(replenerate_hostnames(TUNNEL_HOSTS), start=1)}
103+
return {host: f"{tunnel_net}{ip}" for (ip, host)
104+
in enumerate(tunnel_hosts, start=1)}
105105

106106

107107
def unprivilegify_port(port):
@@ -111,16 +111,16 @@ def unprivilegify_port(port):
111111
return port + 8000 if port < 1024 else port
112112

113113

114-
def panametric_fan_ports(unprivilegify=True):
114+
def panametric_fan_ports(unprivilegify=True, *, tunnel_hosts, tunnel_net):
115115
"""
116-
Attaches the malleable logarithmic casing surrounding TUNNEL_HOST marzlevanes
116+
Attaches the malleable logarithmic casing surrounding tunnel_host marzlevanes
117117
onto the semi-boloid slots of SSH command line syntax.
118118
"""
119-
replenerated_ports = {replenerate_hostname(h): p for (h, p) in TUNNEL_HOSTS.items()}
120119
return itertools.chain(*[
121120
["-L", f"{ip}:{unprivilegify_port(port) if unprivilegify else port}:{host}:{port}"]
122-
for (host, ip) in prefabulate_tunnel().items()
123-
for port in replenerated_ports[host]])
121+
for (host, ip) in prefabulate_tunnel(tunnel_hosts=tunnel_hosts,
122+
tunnel_net=tunnel_net).items()
123+
for port in tunnel_hosts[host]])
124124

125125

126126
def surmount_host_line(host, ip, *, dest=None):
@@ -137,20 +137,20 @@ def surmount_host_line(host, ip, *, dest=None):
137137
# In IPv4, all of 127.0.0.0/8 is reserved for loopback. In IPv6, there is
138138
# exactly *one* loopback address, ::1/128. If you think this is sadlarious,
139139
# I agree.)
140-
def apply_encabulation(lines, *, port_forwarding_dingle_arm=False, dest='ulsfo'):
140+
def apply_encabulation(lines, *, port_forwarding_dingle_arm=False, dest,
141+
text_cdn_hosts, tunnel_hosts, tunnel_net):
141142
"""A function to be passed to rewrite_hosts, mostly."""
142143
text_ip = socket.gethostbyname(f"text-lb.{dest}.wikimedia.org")
143144

144145
tunnel_lines = []
145146
if port_forwarding_dingle_arm:
146147
tunnel_lines = [surmount_host_line(host, ip, dest="ssh") for (host, ip)
147-
in prefabulate_tunnel().items()]
148+
in prefabulate_tunnel(tunnel_hosts, tunnel_net=tunnel_net).items()]
148149

149150
return itertools.chain(
150151
lines,
151152
[MAGIC],
152-
[surmount_host_line(host, text_ip, dest=dest)
153-
for host in replenerate_hostnames(TEXT_CDN_HOSTS)],
153+
[surmount_host_line(host, text_ip, dest=dest) for host in text_cdn_hosts],
154154
tunnel_lines,
155155
[MAGIC])
156156

@@ -203,10 +203,18 @@ def main(args):
203203
dcs.remove(current_dc)
204204
dest = dcs[0]
205205

206+
text_cdn_hosts = replenerate_hostnames(TEXT_CDN_HOSTS)
207+
tunnel_hosts = {replenerate_hostname(h): p for (h, p) in TUNNEL_HOSTS.items()}
208+
if args.tunnel_everything:
209+
tunnel_hosts.update({h: [443] for h in text_cdn_hosts})
210+
text_cdn_hosts = []
211+
206212
# To avoid weird inconsistencies, always begin by undoing encabulation.
207213
rewrite_hosts(
208214
lambda x: apply_encabulation(undo_encabulation(x),
209-
port_forwarding_dingle_arm=args.ssh_tunnel, dest=dest),
215+
port_forwarding_dingle_arm=args.ssh_tunnel, dest=dest,
216+
text_cdn_hosts=text_cdn_hosts, tunnel_hosts=tunnel_hosts,
217+
tunnel_net=TUNNEL_NET),
210218
etchosts=args.etc_hosts)
211219

212220
try:
@@ -216,6 +224,8 @@ def main(args):
216224
if args.ssh_tunnel:
217225
print("Beginning encabulation of SSH tunnels now... make sure you authenticate.")
218226

227+
prefabulated_tunnels = prefabulate_tunnel(tunnel_hosts, tunnel_net=TUNNEL_NET)
228+
219229
# On MacOS, we need to both alias a bunch of loopback addresses, and also there's no
220230
# good way to bind to privileged ports as non-root. So we kludge with socat.
221231
if platform.system() == "Darwin":
@@ -225,33 +235,36 @@ def main(args):
225235
return
226236

227237
[subprocess.run(["/usr/bin/sudo", "/sbin/ifconfig", "lo0", "alias", ip, "up"],
228-
check=True) for ip in prefabulate_tunnel().values()]
238+
check=True) for ip in prefabulated_tunnels.values()]
229239

230-
replenerated_ports = {replenerate_hostname(h): p for (h, p) in TUNNEL_HOSTS.items()}
240+
use_socat = args.force_socat or platform.system() == "Darwin"
241+
if use_socat:
242+
# Run a socat for each privileged port; let ssh directly handle the unprivileged.
231243
socat_commands = [
232244
["/usr/bin/sudo", "socat",
233245
f"tcp4-listen:{port},fork,reuseaddr,bind={ip},su=nobody",
234246
f"tcp4:{ip}:{unprivilegify_port(port)}"]
235-
for (host, ip) in prefabulate_tunnel().items()
236-
for port in replenerated_ports[host] if unprivilegify_port(port) != port]
247+
for (host, ip) in prefabulated_tunnels.items()
248+
for port in tunnel_hosts[host] if unprivilegify_port(port) != port]
237249
socat_procs = [subprocess.Popen(cmd) for cmd in socat_commands]
238250

239251
ssh_command = list(itertools.chain(
240252
["/usr/bin/ssh", "-N", BASTIONS[dest]],
241253
shlex.split(args.ssh_args) if args.ssh_args else [],
242-
panametric_fan_ports(unprivilegify=(platform.system() == "Darwin"))))
254+
panametric_fan_ports(unprivilegify=use_socat, tunnel_hosts=tunnel_hosts,
255+
tunnel_net=TUNNEL_NET)))
243256

244257
# On Linux we can skip the socat nonsense and instead have capsh invoke ssh with
245258
# CAP_NET_BIND_SERVICE.
246-
if platform.system() == "Linux":
259+
if not use_socat:
247260
ssh_command = [
248261
"/usr/bin/sudo", "-E", "/usr/sbin/capsh", f"--user={os.getlogin()}",
249262
"--inh=cap_net_bind_service", "--addamb=cap_net_bind_service", "--", "-c",
250263
" ".join(ssh_command)]
251264

252265
subprocess.run(ssh_command)
253266

254-
if platform.system() == "Darwin":
267+
if use_socat:
255268
[p.wait() for p in socat_procs]
256269
else:
257270
while not args.no_foreground:
@@ -276,6 +289,10 @@ def main(args):
276289
"marzlevanes, by employing the special mechanism of "
277290
"a port forwarding dingle arm.")
278291

292+
parser.add_argument("--tunnel-everything", action="store_true", default=False,
293+
help="Tunnel all hosts over SSH, not just the usual ones. "
294+
"Implies --ssh-tunnel.")
295+
279296
parser.add_argument("-d", "--datacenter", choices=sorted(BASTIONS),
280297
help="Specify a particular target datacenter. If not specified, defaults "
281298
"to one that is not your normal lotus-o-delta GeoDNS site.")
@@ -291,7 +308,9 @@ def main(args):
291308

292309
parser.add_argument("--ssh-args", help="Extra arguments to pass to the ssh girdle spring")
293310

311+
# Some special arguments just for manual testing
294312
parser.add_argument("--etc-hosts", default="/etc/hosts", help=argparse.SUPPRESS)
313+
parser.add_argument("--force-socat", action="store_true", default=False, help=argparse.SUPPRESS)
295314

296315
# Specify output of "--version"
297316
parser.add_argument(
@@ -303,4 +322,6 @@ def main(args):
303322
if args.no_foreground and args.ssh_tunnel:
304323
print("Sorry, -f/--no-foreground and -s/--ssh-tunnel are incompatible :(")
305324
sys.exit(2)
325+
if args.tunnel_everything:
326+
args.ssh_tunnel = True
306327
main(args)

0 commit comments

Comments
 (0)