This repository has been archived by the owner on Jul 9, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathRouter.java
1273 lines (1073 loc) · 47.6 KB
/
Router.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package profile;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.Vector;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.swing.JOptionPane;
import core.data.InterfaceData;
import core.iface.IUnit;
import core.model.DeviceModel;
import core.model.FirewallModel;
import core.model.InterfaceModel;
import core.model.MachineModel;
import core.model.NetworkModel;
import core.model.ServerModel;
import core.profile.AStructuredProfile;
import core.unit.fs.FileOwnUnit;
import core.unit.fs.FilePermsUnit;
import core.unit.fs.FileUnit;
import core.unit.pkg.InstalledUnit;
public class Router extends AStructuredProfile {
private DNS dns;
private DHCP dhcp;
private QoS qos;
private FirewallModel firewall;
private Vector<String> userIfaces;
private Vector<String> wanIfaces;
private HashMap<String, InetAddress[]> resolved;
private String domain;
private boolean isPPP;
private boolean isStatic;
public Router(ServerModel me, NetworkModel networkModel) {
super("router", me, networkModel);
this.dns = new DNS(me, networkModel);
this.dhcp = new DHCP(me, networkModel);
this.qos = new QoS(me, networkModel);
//:2+ is a wildcard for VPN traffic interfaces
this.userIfaces = new Vector<String>();
this.userIfaces.addElement(":2+");
this.wanIfaces = new Vector<String>();
this.isPPP = false;
this.isStatic = false;
this.resolved = new HashMap<String, InetAddress[]>();
}
public DHCP getDHCP() {
return this.dhcp;
}
public DNS getDNS() {
return this.dns;
}
public Vector<IUnit> getPersistentConfig() {
Vector<IUnit> units = new Vector<IUnit>();
String sysctl = "";
sysctl += "net.ipv4.ip_forward=1\n";
sysctl += "net.ipv6.conf.all.disable_ipv6=1\n";
sysctl += "net.ipv6.conf.default.disable_ipv6=1\n";
sysctl += "net.ipv6.conf.lo.disable_ipv6=1";
units.addElement(((ServerModel)me).getConfigsModel().addConfigFile("sysctl", "proceed", sysctl, "/etc/sysctl.conf"));
units.addAll(this.dhcp.getPersistentConfig());
units.addAll(this.dns.getPersistentConfig());
units.addAll(this.qos.getPersistentConfig());
units.addAll(routerScript());
return units;
}
protected Vector<IUnit> getInstalled() {
Vector<IUnit> units = new Vector<IUnit>();
//units.addElement(new InstalledUnit("xsltproc", "xsltproc"));
units.addElement(new InstalledUnit("traceroute", "traceroute"));
units.addElement(new InstalledUnit("speedtest_cli", "speedtest-cli"));
units.addAll(this.dns.getInstalled());
units.addAll(this.dhcp.getInstalled());
units.addAll(this.qos.getInstalled());
return units;
}
protected Vector<IUnit> getLiveConfig() {
Vector<IUnit> units = new Vector<IUnit>();
units.addAll(this.dhcp.getLiveConfig());
units.addAll(this.dns.getLiveConfig());
units.addAll(this.qos.getLiveConfig());
units.addAll(dailyBandwidthEmailDigestUnits());
return units;
}
private InetAddress[] hostToInetAddress(String uri) {
InetAddress[] destination = null;
if (this.resolved.containsKey(uri)) {
destination = this.resolved.get(uri);
}
else {
destination = networkModel.stringToAllIPs(uri);
this.resolved.put(uri, destination);
}
return destination;
}
private Vector<IUnit> machineIngressRules(MachineModel machine) {
Vector<IUnit> units = new Vector<IUnit>();
HashMap<String, Set<Integer>> ingress = machine.getRequiredIngress();
for (String uri : ingress.keySet()) {
InetAddress[] destinations = hostToInetAddress(uri);
Integer cidr = machine.getCIDR(uri);
String setName = networkModel.getIPSet().getSetName(name);
networkModel.getIPSet().addToSet(setName, cidr, new Vector<InetAddress>(Arrays.asList(destinations)));
String rule = "";
rule += "-p tcp";
rule += (ingress.get(uri).isEmpty() || ingress.get(uri).contains(0)) ? "" : " -m multiport --dports " + collection2String(ingress.get(uri));
rule += (uri.equals("255.255.255.255")) ? "" : " -m set --match-set " + setName + " src";
rule += " -j ACCEPT";
this.firewall.addFilter(
machine.getHostname() + "_" + setName + "_ingress",
machine.getIngressChain(),
rule,
"Allow call in to " + uri
);
}
return units;
}
private Vector<IUnit> machineEgressRules(MachineModel machine) {
Vector<IUnit> units = new Vector<IUnit>();
HashMap<String, HashMap<Integer, Set<Integer>>> egress = machine.getRequiredEgress();
for (String uri : egress.keySet()) {
InetAddress[] destinations = hostToInetAddress(uri);
String setName = networkModel.getIPSet().getSetName(uri);
networkModel.getIPSet().addToSet(setName, machine.getCIDR(uri), new Vector<InetAddress>(Arrays.asList(destinations)));
String rule = "";
rule += "-p tcp";
rule += (egress.get(uri).values().isEmpty() || collection2String(egress.get(uri).values()).equals("0")) ? "" : " -m multiport --dports " + collection2String(egress.get(uri).values());
rule += (uri.equals("255.255.255.255")) ? "" : " -m set --match-set " + setName + " dst";
rule += " -j ACCEPT";
this.firewall.addFilter(
machine.getHostname() + "_" + setName + "_egress",
machine.getEgressChain(),
rule,
"Allow call out to " + uri
);
}
return units;
}
private Vector<IUnit> serverForwardRules(ServerModel server) {
Vector<IUnit> units = new Vector<IUnit>();
HashMap<String, Set<Integer>> forward = server.getRequiredForward();
for (String destination : forward.keySet()) {
MachineModel destinationMachine = networkModel.getMachineModel(destination);
String request = "";
request += "-p tcp";
request += " -m tcp";
request += " -m multiport";
request += " --sports " + collection2String(forward.get(destination));
request += " -s " + collection2String(destinationMachine.getAddresses());
request += " -d " + collection2String(server.getAddresses());
request += " -j ACCEPT";
String reply = "";
reply += "-p tcp";
reply += " -m tcp";
reply += " -m multiport";
reply += " --dports " + collection2String(forward.get(destination));
reply += " -d " + collection2String(destinationMachine.getAddresses());
reply += " -s " + collection2String(server.getAddresses());
reply += " -j ACCEPT";
this.firewall.addFilter(
server.getHostname() + "_" + destinationMachine.getHostname() + "_forward",
server.getForwardChain(),
request,
"Allow traffic from " + destination
);
this.firewall.addFilter(
destinationMachine.getHostname() + "_" + server.getHostname() + "_forward",
destinationMachine.getForwardChain(),
request,
"Allow traffic to " + destination
);
this.firewall.addFilter(
server.getHostname() + "_" + destinationMachine.getHostname() + "_forward",
server.getForwardChain(),
reply,
"Allow traffic from " + destination
);
this.firewall.addFilter(
destinationMachine.getHostname() + "_" + server.getHostname() + "_forward",
destinationMachine.getForwardChain(),
reply,
"Allow traffic to " + destination
);
}
return units;
}
private Vector<IUnit> machineDnatRules(MachineModel machine) {
Vector<IUnit> units = new Vector<IUnit>();
HashMap<String, Set<Integer>> dnat = machine.getRequiredDnat();
//Only create these rules if we actually *have* users.
if (!networkModel.getIPSet().isEmpty("user")) {
for (String destinationName : dnat.keySet()) {
MachineModel destinationMachine = networkModel.getMachineModel(destinationName);
String rule = "";
rule += "-p tcp";
rule += " -m tcp";
rule += " -m multiport";
rule += " --dports " + collection2String(dnat.get(destinationName));
rule += " ! -s " + collection2String(machine.getAddresses());
rule += " -d " + collection2String(destinationMachine.getAddresses());
rule += " -j DNAT";
rule += " --to-destination " + collection2String(machine.getAddresses());
this.firewall.addNatPrerouting(
machine.getHostname() + "_" + destinationMachine.getHostname() + "_dnat",
rule,
"DNAT traffic for " + destinationName + " to " + machine.getHostname()
);
}
}
//If we've given it an external IP, it's listening, and a request comes in from the outside world, let it have it!
if (networkModel.getData().getExternalIp(machine.getLabel()) != null && !machine.getRequiredListenTCP().isEmpty()) {
String rule = "";
rule += "-i " + collection2String(me.getNetworkData().getWanIfaces(getLabel()));
rule += (this.isStatic) ? " -d " + networkModel.getData().getExternalIp(machine.getLabel()).getHostAddress() : "";
rule += " -p tcp";
rule += " -m multiport";
rule += " --dports " + collection2String(machine.getRequiredListenTCP());
rule += " -j DNAT";
rule += " --to-destination " + collection2String(machine.getAddresses());
this.firewall.addNatPrerouting(
machine.getHostname() + "_external_ip_dnat",
rule,
"DNAT external traffic on " + networkModel.getData().getExternalIp(machine.getLabel()).getHostAddress() + " to " + machine.getHostname() + " if it has an external IP & is listening"
);
}
return units;
}
private Vector<IUnit> machineAllowUserForwardRules(MachineModel machine) {
Vector<IUnit> units = new Vector<IUnit>();
Vector<Integer> listen = machine.getRequiredListenTCP();
String machineName = machine.getLabel();
//Only create these rules if we actually *have* users.
if (networkModel.getIPSet().isEmpty("user")) { return units; }
if (machine instanceof ServerModel && listen.size() > 0) {
String rule = "";
rule += "-p tcp";
rule += " -m multiport";
rule += " --dports " + collection2String(listen);
rule += " -m set";
rule += " --match-set user src";
rule += " -j ACCEPT";
this.firewall.addFilter(
machineName + "_users_forward",
machine.getForwardChain(),
rule,
"Allow traffic from users"
);
}
else if (machine instanceof DeviceModel && networkModel.getInternalOnlyDevices().contains(machine)) {
//First, iterate through everything which should be listening for everyone
String listenRule = "";
listenRule += "-p tcp";
listenRule += (!listen.isEmpty()) ? " -m multiport --dports " + collection2String(listen) : "";
listenRule += " -m set";
listenRule += " --match-set user src";
listenRule += " -j ACCEPT";
this.firewall.addFilter(
machineName + "_users_forward",
machine.getForwardChain(),
listenRule,
"Allow traffic from users"
);
//These are management ports
Set<Integer> ports = ((DeviceModel) machine).getManagementPorts();
if (ports != null && !ports.isEmpty()) {
String managementRule = "";
managementRule += "-p tcp";
managementRule += " -m multiport --dports " + collection2String(ports);
managementRule += " -m set";
managementRule += " --match-set " + machineName + "_admins src";
managementRule += " -j ACCEPT";
this.firewall.addFilter(
machineName + "_admins_management_forward",
machine.getForwardChain(),
managementRule,
"Allow management traffic from admins"
);
}
}
return units;
}
private Vector<IUnit> machineIngressEgressForwardRules(MachineModel machine) {
Vector<IUnit> units = new Vector<IUnit>();
String wanIfaces = collection2String(networkModel.getData().getWanIfaces(me.getLabel()));
String ingressRule = "";
ingressRule += "-i " + wanIfaces;
ingressRule += " -j " + machine.getIngressChain();
String egressRule = "";
egressRule += "-o " + wanIfaces;
egressRule += " -j " + machine.getEgressChain();
this.firewall.addFilter(
machine.getHostname() + "_jump_on_ingress",
machine.getForwardChain(),
ingressRule,
"Jump to our ingress chain for incoming (external) traffic"
);
this.firewall.addFilter(
machine.getHostname() + "_jump_on_egress",
machine.getForwardChain(),
egressRule,
"Jump to our egress chain for outgoing (external) traffic"
);
return units;
}
private Vector<IUnit> userAllowServerForwardRules(DeviceModel user) {
Vector<IUnit> units = new Vector<IUnit>();
if (!networkModel.getAllServers().isEmpty()) {
String rule = "";
rule += "-m set";
rule += " --match-set servers dst";
rule += " -j ACCEPT";
this.firewall.addFilter(
user.getHostname() + "_servers_forward",
user.getForwardChain(),
rule,
"Allow traffic to servers"
);
}
return units;
}
private Vector<IUnit> userAllowInternalOnlyForwardRules(DeviceModel user) {
Vector<IUnit> units = new Vector<IUnit>();
if (!networkModel.getInternalOnlyDevices().isEmpty()) {
String rule = "";
rule += "-m set";
rule += " --match-set internalonly dst";
rule += " -j ACCEPT";
this.firewall.addFilter(
user.getHostname() + "_internalonly_forward",
user.getForwardChain(),
rule,
"Allow traffic to internal-only devices"
);
}
return units;
}
private Vector<IUnit> serverAdminRules(MachineModel machine) {
Vector<IUnit> units = new Vector<IUnit>();
String machineName = machine.getLabel();
//We need to check there's anything in the set, first
if (networkModel.getIPSet().isEmpty(machineName + "_admins")) {
if (((ServerModel)machine).isRouter() && ((ServerModel)machine).isMetal()) {
String rule = "";
rule += "-p tcp";
rule += " --dport " + networkModel.getData().getSSHPort(machineName);
rule += " -j ACCEPT";
this.firewall.addFilter(
machine.getHostname() + "_allow_admin_ssh",
machine.getForwardChain(),
rule,
"Allow SSH from admins"
);
}
else {
//Hmm. Should probably throw something here
}
}
else {
String rule = "";
rule += "-p tcp";
rule += " --dport " + networkModel.getData().getSSHPort(machineName);
rule += " -m set";
rule += " --match-set " + machineName + "_admins src";
rule += " -j ACCEPT";
this.firewall.addFilter(
machine.getHostname() + "_allow_admin_ssh",
machine.getForwardChain(),
rule,
"Allow SSH from admins"
);
}
return units;
}
private Vector<IUnit> networkIptUnits() {
Vector<IUnit> units = new Vector<IUnit>();
for (ServerModel server : networkModel.getAllServers()) {
machineIngressRules(server);
machineEgressRules(server);
serverForwardRules(server);
machineDnatRules(server);
machineAllowUserForwardRules(server);
serverAdminRules(server);
}
for (DeviceModel device : networkModel.getUserDevices()) {
if (device.getSubnets().isEmpty()) { continue; } //Unless they don't have any interfaces
//No need for further ingress rules here
machineDnatRules(device);
machineAllowUserForwardRules(device);
userAllowServerForwardRules(device);
userAllowInternalOnlyForwardRules(device);
machineEgressRules(device);
}
for (DeviceModel device : networkModel.getInternalOnlyDevices()) {
//No need for ingress or egress rules here, they only listen on fwd
machineDnatRules(device); //May be behind a load balancer
machineAllowUserForwardRules(device);
}
for (DeviceModel device : networkModel.getExternalOnlyDevices()) {
//No need for forward or ingress rules here
machineDnatRules(device); //May be behind a load balancer
machineAllowUserForwardRules(device);
machineEgressRules(device);
}
for (MachineModel machine : networkModel.getAllMachines()) {
//Make sure to push traffic to {in,e}gress chains
if (machine.getSubnets().isEmpty()) { continue; } //Unless they don't have any interfaces
machineIngressEgressForwardRules(machine);
}
if (networkModel.getData().getAutoGuest()) {
DeviceModel autoguest = new DeviceModel("autoguest", networkModel);
autoguest.setCIDR(22);
autoguest.setFirstOctet(10);;
autoguest.setSecondOctet(250);
autoguest.setThirdOctet(0);
autoguest.getInterfaceModel().addIface(new InterfaceData(
"autoguest", //host
"lan0:9001", //iface
null, //mac
"static", //inet
null, //bridgeports
networkModel.stringToIP("10.250.0.0"), //subnet
networkModel.stringToIP("10.250.0.0"), //address
networkModel.stringToIP("255.255.252.0"), //netmask
null, //broadcast
networkModel.stringToIP("10.0.0.1"), //gateway
"Auto Guest pool" //comment
));
baseIptConfig(autoguest);
networkModel.getIPSet().addToSet("autoguest", 22, networkModel.stringToIP("10.250.0.0"));
String rule = "";
rule += "-p tcp";
rule += " -m set --match-set autoguest src";
rule += " -j ACCEPT";
this.firewall.addFilter(
autoguest.getEgressChain(),
"autoguest_egress",
rule,
"Allow automatic guest pool to call out to the internet"
);
machineIngressEgressForwardRules(autoguest);
}
return units;
}
private String collection2String(Object collection) {
if (collection instanceof HashMap) {
collection = ((HashMap<?, ?>) collection).keySet();
}
return collection.toString()
.replace("[", "")
.replace("]", "")
.replace(" ", "")
.replace("/", "")
.replace("null,", "")
.replace(",null", "");
}
private String buildUserDailyBandwidthEmail(String sender, String subject, DeviceModel user, Boolean includeBlurb) {
String email = "";
email += "echo -e \\\"";
email += "subject:" + subject + "\\n";
email += "from:" + sender + "\\n";
email += "recipients:" + user.getEmailAddress() + "\\n";
email += "\\n";
email += "UL: \\`iptables -L " + user.getEgressChain() + " -v -n | tail -n 2 | head -n 1 | awk '{ print \\$2 }'\\`\\n";
email += "DL: \\`iptables -L " + user.getIngressChain() + " -v -n | tail -n 2 | head -n 1 | awk '{ print \\$2 }'\\`";
if (includeBlurb) {
email += "\\n";
email += "\\n";
email += "=====================================================================================";
email += "\\n";
email += "\\n";
email += "As you know, one of the key advantages afforded by operating our network through Thornsec is that it monitors for uploading traffic. ";
email += "The reason for this is that we want to try and check for any data exfiltration from our network.";
email += "\\n";
email += "\\n";
email += "Part of this monitoring allows our router to give you a daily digest of how much you've downloaded and uploaded.";
email += "\\n";
email += "\\n";
email += "This is not a punitive email, this information is not logged, and is only sent to you, as a user. This will hopefully alert you to any strange activity ";
email += "coming from your device.";
email += "\\n";
email += "\\n";
email += "If you think there is something suspicious about the figures above, please forward this email to the Tech Team so they can look into it for you.";
email += "\\n";
email += "\\n";
email += "Thanks!";
email += "\\n";
email += "Tech Team";
}
email += "\\\"";
email += "|sendmail \"" + user.getEmailAddress() + "\"\n\n";
return email;
}
private Vector<IUnit> dailyBandwidthEmailDigestUnits() {
Vector<IUnit> units = new Vector<IUnit>();
String script = "";
script += "#!/bin/bash\n";
//Iterate through users first; they need alerting individually
for (DeviceModel user : networkModel.getUserDevices()) {
if (!networkModel.getDeviceModel(user.getLabel()).getInterfaces().isEmpty()) { //But only if they're an internal user
//Email the user only
script += "\n\n";
script += buildUserDailyBandwidthEmail(networkModel.getData().getAdminEmail(),
"[" + user.getLabel() + "." + networkModel.getData().getLabel() + "] Daily Bandwidth Digest",
user,
true);
script += "iptables -Z " + user.getIngressChain() + "\n";
script += "iptables -Z " + user.getEgressChain();
}
}
script += "\n\n";
script += "echo -e \\\"";
script += "subject: [" + networkModel.getData().getLabel() + "." + networkModel.getData().getDomain(me.getLabel()) + "] Daily Bandwidth Digest\\n";
script += "from:" + me.getLabel() + "@" + networkModel.getData().getDomain(me.getLabel()) + "\\n";
script += "recipients:" + networkModel.getData().getAdminEmail() + "\\n";
//Iterate through everything which should be reported back to admins.
//This used to be an individual email per device/server, but this is useless as it just spams the admins
for (DeviceModel peripheral : networkModel.getAllPeripheralDevices()) {
script += "\\n\\n";
script += "Digest for " + peripheral.getLabel() + ":\\n";
script += "UL: \\`iptables -L " + peripheral.getEgressChain() + " -v -n | tail -n 2 | head -n 1 | awk '{ print \\$2 }'\\`\\n";
script += "DL: \\`iptables -L " + peripheral.getIngressChain() + " -v -n | tail -n 2 | head -n 1 | awk '{ print \\$2 }'\\`";
}
//Then servers
for (ServerModel srv : networkModel.getAllServers()) {
script += "\\n\\n";
script += "Digest for " + srv.getLabel() + ":\\n";
script += "UL: \\`iptables -L " + srv.getEgressChain() + " -v -n | tail -n 2 | head -n 1 | awk '{ print \\$2 }'\\`\\n";
script += "DL: \\`iptables -L " + srv.getIngressChain() + " -v -n | tail -n 2 | head -n 1 | awk '{ print \\$2 }'\\`";
}
script += "\\\"";
script += "|sendmail \"" + networkModel.getData().getAdminEmail() + "\"\n";
//Zero all the chains
for (MachineModel machine : networkModel.getAllMachines()) {
script += "\niptables -Z " + machine.getIngressChain();
script += "\niptables -Z " + machine.getEgressChain();
}
units.addElement(new FileUnit("daily_bandwidth_alert_script_created", "proceed", script, "/etc/cron.daily/bandwidth", "I couldn't create the bandwidth digest script. This means you and your users won't receive daily updates on bandwidth use"));
units.addElement(new FilePermsUnit("daily_bandwidth_alert_script", "daily_bandwidth_alert_script_created", "/etc/cron.daily/bandwidth", "750", "I couldn't set the bandwidth digest script to be executable. This means you and your users won't receive daily updates on bandwidth use"));
return units;
}
public Vector<IUnit> getNetworking() {
Vector<IUnit> units = new Vector<IUnit>();
units.addElement(new FileUnit("leave_my_resolv_conf_alone", "proceed",
"make_resolv_conf() { :; }",
"/etc/dhcp/dhclient-enter-hooks.d/leave_my_resolv_conf_alone"));
units.addElement(new FilePermsUnit("leave_my_resolv_conf_alone", "leave_my_resolv_conf_alone",
"/etc/dhcp/dhclient-enter-hooks.d/leave_my_resolv_conf_alone",
"755",
"I couldn't stop various systemd services deciding to override your DNS settings. This will cause you intermittent, difficult to diagnose problems as it randomly sets your DNS to wherever it decides. Great for desktops, atrocious for servers..."));
InterfaceModel interfaces = me.getInterfaceModel();
firewall = ((ServerModel)me).getFirewallModel();
domain = networkModel.getData().getDomain(me.getLabel());
JsonArray extInterfaces = (JsonArray) networkModel.getData().getPropertyObjectArray(me.getLabel(), "wan");
if (extInterfaces.size() == 0) {
JOptionPane.showMessageDialog(null, "You must specify at least one WAN interface for your router.\n\nValid options are 'ppp', 'static', and 'dhcp'");
System.exit(1);
}
for (int i = 0; i < extInterfaces.size(); ++i) {
JsonObject row = extInterfaces.getJsonObject(i);
String wanIface = row.getString("iface");
//If we've already declared this iface, give it an alias
if (wanIfaces.contains(wanIface)) {
wanIface += ":" + i;
}
//These are fine to be null if not given
InetAddress staticAddress = networkModel.stringToIP(row.getString("address", null));
InetAddress netmask = networkModel.stringToIP(row.getString("netmask", null));
InetAddress gateway = networkModel.stringToIP(row.getString("gateway", null));
InetAddress broadcast = networkModel.stringToIP(row.getString("broadcast", null));
switch (row.getString("inettype", null)) {
case "dhcp":
interfaces.addIface(new InterfaceData(
me.getLabel(), //host
wanIface, //iface
null, //mac
"dhcp", //inet
null, //bridgeports
null, //subnet
null, //address
null, //netmask
null, //broadcast
null, //gateway
"DHCP WAN physical network interface" //comment
));
String dhclient = "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n";
dhclient += "send host-name = gethostname();\n";
dhclient += "supersede domain-search \\\"" + this.domain + "\\\";\n";
dhclient += "supersede domain-name-servers 10.0.0.1;\n";
dhclient += "request subnet-mask, broadcast-address, time-offset, routers,\n";
dhclient += " domain-name, domain-name-servers, domain-search, host-name,\n";
dhclient += " dhcp6.name-servers, dhcp6.domain-search,\n";
dhclient += " netbios-name-servers, netbios-scope, interface-mtu,\n";
dhclient += " rfc3442-classless-static-routes, ntp-servers;";
units.addElement(new FileUnit("router_ext_dhcp_persist", "proceed", dhclient, "/etc/dhcp/dhclient.conf"));
firewall.addFilterInput("router_ext_dhcp_in",
"-i " + wanIface
+ " -d 255.255.255.255"
+ " -p udp"
+ " --dport 68"
+ " --sport 67"
+ " -j ACCEPT",
"Make sure the Router can send DHCP requests");
firewall.addFilterOutput("router_ext_dhcp_ipt_out",
"-o " + wanIface
+ " -p udp"
+ " --dport 67"
+ " --sport 68"
+ " -j ACCEPT",
"Make sure the Router can receive DHCP responses");
break;
case "ppp":
units.addElement(new InstalledUnit("ext_ppp", "ppp"));
units.addElement(me.getInterfaceModel().addPPPIface("router_ext_ppp_iface", wanIface));
((ServerModel)me).getProcessModel().addProcess("/usr/sbin/pppd call provider$");
((ServerModel)me).getConfigsModel().addConfigFilePath("/etc/ppp/peers/dsl-provider$");
((ServerModel)me).getConfigsModel().addConfigFilePath("/etc/ppp/options$");
units.addElement(((ServerModel)me).getConfigsModel().addConfigFile("resolv_conf", "proceed", "nameserver 127.0.0.1", "/etc/ppp/resolv.conf"));
firewall.addMangleForward("clamp_mss_to_pmtu",
"-p tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu",
"Clamp the MSS to PMTU. This makes sure the packets over PPPoE are the correct size (and take into account the PPPoE overhead)");
this.isPPP = true;
break;
case "static":
interfaces.addIface(new InterfaceData(
me.getLabel(), //host
wanIface, //iface
null, //mac
"static", //inet
null, //bridgeports
null, //subnet
staticAddress, //address
netmask, //netmask
broadcast, //broadcast
gateway, //gateway
"Static WAN physical network interface" //comment
));
this.isStatic = true;
break;
default:
JOptionPane.showMessageDialog(null, "Valid options for your router's WAN inettype are 'ppp', 'static', and 'dhcp'");
System.exit(1);
break;
}
wanIfaces.addElement(wanIface);
}
Vector<String> routerLanIfaces = new Vector<String>(networkModel.getData().getLanIfaces(me.getLabel()).keySet());
String lanBridge = "lan0";
InetAddress netmask = networkModel.getData().getNetmask();
//We don't actually care about LAN ifaces if we're a metal/router
if (((ServerModel) me).isMetal()) {
//We very deliberately hang everything off a bridge, to stop internal traffic over the WAN iface
units.addElement(new InstalledUnit("bridge_utils", "bridge-utils"));
InetAddress subnet = networkModel.stringToIP("10.0.0.0");
InetAddress address = networkModel.stringToIP("10.0.0.1");
interfaces.addIface(new InterfaceData(
me.getLabel(), //host
lanBridge, //iface
null, //mac
"static", //inet
new String[] {"none"}, //bridgeports
subnet, //subnet
null, //address
netmask, //netmask
null, //broadcast
address, //"gateway" - because this is a router iface, it only looks at gateways
"VM interface on a bridge to nowhere" //comment
));
}
else {
//First, add this machine's own interfaces
for (String lanIface : routerLanIfaces) {
interfaces.addIface(new InterfaceData(
me.getLabel(), //host
lanIface, //iface
null, //mac
"manual", //inet
null, //bridgeports
null, //subnet
null, //address
null, //netmask
null, //broadcast
null, //gateway
"LAN physical network interface" //comment
));
}
//Now, bridge 'em
InetAddress subnet = networkModel.stringToIP("10.0.0.0");
InetAddress address = networkModel.stringToIP("10.0.0.1");
interfaces.addIface(new InterfaceData(
"lan", //host
lanBridge, //iface
null, //mac
"static", //inet
routerLanIfaces.toArray(new String[routerLanIfaces.size()]), //bridgeports
subnet, //subnet
address, //address
netmask, //netmask
null, //broadcast
address, //gateway
"bridge all physical interfaces" //comment
));
}
//Now add for our servers
//for (ServerModel srv : networkModel.getAllServers()) {
for (MachineModel machine : networkModel.getAllMachines()) {
if (machine.equals(me)) { continue; } //Skip if we're talking about ourself
Integer classifier = null;
if (machine instanceof ServerModel) {
classifier = 0;
}
else if (machine instanceof DeviceModel) {
classifier = 1;
}
for (InterfaceData machineLanIface : machine.getInterfaceModel().getIfaces()) {
//Parse our MAC address into an integer to stop collisions when adding/removing interfaces
// String alias = null; //getAlias(machineLanIface.getMac());
//if (machineLanIface.getMac() == null) {
// alias = getAlias(machineLanIface.getIface());
//}
//else {
// alias = getAlias(machineLanIface.getMac());
// }
String ifaceName = null;
String ifaceComment = null;
if (((ServerModel) me).isMetal()) {
ifaceName = "vm" + machine.getThirdOctet();
ifaceComment = "Router/Metal interface. This is a fake interface just for the VM";
}
else {
ifaceName = lanBridge + ":" + classifier + machine.getThirdOctet();
ifaceComment = "Router interface. Let's bridge to lan";
}
interfaces.addIface(new InterfaceData(
machine.getLabel(), //host
ifaceName, //iface
machineLanIface.getMac(), //mac
"static", //inet
machineLanIface.getBridgePorts(), //bridgeports
machineLanIface.getSubnet(), //subnet
machineLanIface.getAddress(), //address
netmask, //netmask
machineLanIface.getBroadcast(), //broadcast
machineLanIface.getGateway(), //gateway
ifaceComment //comment
));
String[] cnames = networkModel.getData().getCnames(machine.getLabel());
String[] subdomains = new String[cnames.length + 1];
System.arraycopy(new String[] {machine.getHostname()},0,subdomains,0, 1);
System.arraycopy(cnames,0,subdomains,1, cnames.length);
this.dns.addDomainRecord(networkModel.getData().getDomain(machine.getLabel()), machineLanIface.getGateway(), subdomains, machineLanIface.getAddress());
}
}
if (networkModel.getData().getAutoGuest()) {
interfaces.addIface(new InterfaceData(
"autoguest", //host
"lan0:9001", //iface
null, //mac
"static", //inet
null, //bridgeports
networkModel.stringToIP("10.250.0.0"), //subnet
null, //address
networkModel.stringToIP("255.255.252.0"), //netmask
null, //broadcast
networkModel.stringToIP("10.250.0.1"), //gateway
"Auto Guest pool, bridged to our lan" //comment
));
}
//Initialise the basic firewall rules for everything
for (MachineModel machine : networkModel.getAllMachines()) {
if (machine.getSubnets().isEmpty()) { continue; } //Unless they don't have any interfaces
baseIptConfig(machine);
}
for (String wanIface : wanIfaces) {
//Masquerade on the external iface
this.firewall.addNatPostrouting(me.getHostname() + "_masquerade_external",
"-o " + wanIface
+ " -j MASQUERADE",
"Mask the IP address of any external traffic coming from our network on " + wanIface + " to obscure internal IPs");
}
//If we're not forcing VPN only, also allow clearnet devices
if (!networkModel.getData().getVpnOnly()) {
userIfaces.addElement(":1+");
}
units.addAll(dhcp.getNetworking());
units.addAll(dns.getNetworking());
units.addAll(qos.getNetworking());
units.addAll(networkIptUnits());
return units;
}
private Vector<IUnit> baseIptConfig(MachineModel machine) {
Vector<IUnit> units = new Vector<IUnit>();
//Do we want to be logging drops?
Boolean debugMode = Boolean.parseBoolean(networkModel.getData().getProperty(me.getLabel(), "debug", false));
//Create our egress chain for bandwidth (exfil?) tracking
//In future, we could perhaps do some form of traffic blocking malarky here?
this.firewall.addChain(machine.getEgressChain(), "filter", machine.getEgressChain());
//Create our ingress chain for download bandwidth tracking
this.firewall.addChain(machine.getIngressChain(), "filter", machine.getIngressChain());
//Create our forward chain for all other rules
this.firewall.addChain(machine.getForwardChain(), "filter", machine.getForwardChain());
//Force traffic to/from a given subnet to jump to our chains
this.firewall.addFilterForward(machine.getHostname() + "_ipt_server_src",
"-s " + machine.getSubnets().elementAt(0).getHostAddress() + "/" + machine.getCIDR()
+ " -j "+ machine.getForwardChain(),
"Force any internal traffic coming from " + machine.getHostname() + " to its own chain");
this.firewall.addFilterForward(machine.getHostname() + "_ipt_server_dst",
"-d " + machine.getSubnets().elementAt(0).getHostAddress() + "/" + machine.getCIDR()
+ " -j " + machine.getForwardChain(),
"Force any internal traffic going to " + machine.getHostname() + " to its own chain");
//We want to default drop anything not explicitly whitelisted
//Make sure that these are the very first rules as the chain may have been pre-populated