-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathforwardportstoguestgenerator.php
276 lines (219 loc) · 9.97 KB
/
forwardportstoguestgenerator.php
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
#!/usr/bin/php
<?PHP
// forwardportstoguestgenerator.php
// v0102
// scan ifconfig and virsh, create iptables directives to forward ports to kvm guests
// chmod this script 755 to run as ./forwardportstoguestgenerator.php or run with php forwardportstoguestgenerator.php
// writes to a text file the BASH script forwardportstoguestscript.sh
// 2021/04/07
// Gordon Buchan https://gordonbuchan.com
// MIT license https://mit-license.org
// overview
// run the command "ifconfig" to isolate potential wan adapter names and ip addresses
// infer the KVM subnet based on the first 3 sections of the ip address of the "virbr0" adapter
// run the command "virsh net-dhcp-leases default" to isolate potential kvm guest names and ip addresses
// ask client to choose WAN adapter
// ask client to choose KVM guest
// create a batch file containing iptables directives to open the virtual adapter to packets from outside the host
// and to forward ports from the host adapter to the KVM guest adapter 80/tcp, and 443/tcp, 8022/tcp
// //////////////////////////////////////////////////////////////////////////////////
// start function sink
// str_contains() polyfill for pre PHP8
if (!function_exists('str_contains')) {
function str_contains(string $haystack, string $needle): bool
{
return '' === $needle || false !== strpos($haystack, $needle);
}
}
// end function sink
// //////////////////////////////////////////////////////////////////////////////////
// start get the WAN adapter names and ip addresses
// capture output of ifconfig command to variable $ifcstr
$ifcstr = `ifconfig`;
// convert string $ifcstr to array of lines $ifcstrarr
// use linefeed as field delimiter in array population
$ifcstrarr = explode("\n",$ifcstr);
// count lines in the array
$ifcstrarrnumlines = count($ifcstrarr);
$adnamestrarr = array();
$adipstrarr = array();
$kvmsubnet = "";
// iterate through array of lines
for ( $i=0;$i<$ifcstrarrnumlines;$i++) {
if ( str_contains($ifcstrarr[$i],"flags")) {
$flagsstr = "flags";
$flagsstrloc = strpos("$ifcstrarr[$i]", $flagsstr) - 2;
$adnamestr = substr($ifcstrarr[$i],0,$flagsstrloc);
} // close if str contains "flags"
// we will eventually filter virbr0, but for now we can find out the subnet for the KVM guest network
if ( str_contains($ifcstrarr[$i],"inet") && !str_contains($ifcstrarr[$i],"inet6") ) {
$inetstr = "inet";
$inetstrloc = strpos("$ifcstrarr[$i]",$inetstr) + 5;
$adipstr = substr($ifcstrarr[$i],$inetstrloc,"20");
$spacestrloc = strpos("$adipstr"," ");
// trimming the variable
$adipstr = substr($adipstr,0,$spacestrloc);
if (str_contains($adnamestr,"virbr0")) {
// start infer KVM subnet
// //////////////////////////////////////////////////////////
// do stuff here to get the virbr0 ip address so we can infer subnet
$kvmsubnetraw = $adipstr;
$lastdotloc = strrpos($kvmsubnetraw,".");
$kvmsubnet = substr($kvmsubnetraw,0,$lastdotloc) . ".0/24";
echo "\nKVM subnet\nkvmsubnet: $kvmsubnet\n\n";
// end infer KVM subnet
// //////////////////////////////////////////////////////////
} else {
// stuff the arrays they will match by number because done at same time
// filter for loopback device
if (!($adipstr == "127.0.0.1")) {
$adnamestrarr[] = $adnamestr;
$adipstrarr[] = $adipstr;
}
}
} // close if str contains "inet"
} // end for $i
//so we are always defined
$adnamestrarrnumlines = "";
$adnamestrarrnumlines = count ($adnamestrarr);
if (!$adnamestrarrnumlines) {
echo "no WAN adapters found.\nStopping.\n";
exit();
}
// if we do not have a KVM subnet, then something is wrong. Stop.
if (!$kvmsubnet) {
echo "KVM subnet not detected. Stopping.\n";
exit();
}
// end get the WAN adapter names and ip addresses
// //////////////////////////////////////////////////////////////////////////////////
// start get the KVM guest names and ip addresses
// capture output of virsh command to variable $ifcstr
$virshleastr = `virsh net-dhcp-leases default`;
// convert string $virshleastr to array of lines $virshleastrarr
// use linefeed as field delimiter in array population
$virshleastrarr = explode("\n",$virshleastr);
// count lines in the array
$virshleastrarrnumlines = count($virshleastrarr);
$kvmnamestrarr = array();
$kvmipstrarr = array();
// iterate through array of lines
for ( $j=0;$j<$virshleastrarrnumlines;$j++) {
if ( str_contains($virshleastrarr[$j],"ipv4")) {
$ipv4str = "ipv4";
$ipv4strloc = strpos("$virshleastrarr[$j]", $ipv4str) + 11;
$kvmlinestr = substr($virshleastrarr[$j],$ipv4strloc,50);
$slashstr = "/";
$slashstrloc = strpos("$kvmlinestr",$slashstr);
$kvmipstr = substr($kvmlinestr,0,$slashstrloc);
$kvmnamestr = substr($kvmlinestr,$slashstrloc+5,12);
$kvmnamestr = trim($kvmnamestr);
//stuff the arrays they will match by number because done at same time
$kvmnamestrarr[] = $kvmnamestr;
$kvmipstrarr[] = $kvmipstr;
} // close if str contains "ipv4"
} // end for $j
$kvmnumlines = count ($kvmnamestrarr);
if (!$kvmnumlines) {
echo "no VM guest DHCP leases found. Please start a VM.\nStopping.\n";
exit();
}
// end get the KVM guest names and ip addresses
// //////////////////////////////////////////////////////////////////////////////////
// start ask client to choose WAN adapter
// show the possible WAN adapters as a numbered list to console:
echo "WAN adapters\n";
for ($k=0;$k<$adnamestrarrnumlines;$k++) {
$displaynum = $k + 1;
echo "$displaynum. $adnamestrarr[$k] $adipstrarr[$k]\n";
}
echo "\n";
// use readline function to ask questions interactively
// trap function in a while condition for sanity checking on input until satisfied
$wananswer = "";
while (!$wananswer || ($wananswer>$displaynum) || !is_numeric($wananswer) ) {
$wananswer = readline("Please choose a WAN adapter (1-$displaynum): ");
}
echo "choice entered: $wananswer\n";
// because humans start at 1 and computers start at 0
$wanchoiceminus = $wananswer - 1;
$wanadaptername = $adnamestrarr[$wanchoiceminus];
$wanadapterip = $adipstrarr[$wanchoiceminus];
echo "\n";
echo "wanadaptername: $wanadaptername\n";
echo "wanadapterip: $wanadapterip\n";
echo "\n";
// end ask client to choose WAN adapter
// //////////////////////////////////////////////////////////////////////////////////
// start ask client to choose KVM guest
// show the possible KVM guests as a numbered list to console:
echo "KVM guests\n";
echo "(hint: if a VM is not listed here, start the VM so it gets a DHCP lease)\n";
for ($m=0;$m<$kvmnumlines;$m++) {
$displaynum = $m + 1;
echo "$displaynum. $kvmnamestrarr[$m] $kvmipstrarr[$m]\n";
}
echo "\n";
// use readline function to ask questions interactively
// trap function in a while condition for sanity checking on input until satisfied
$kvmanswer = "";
while (!$kvmanswer || ($kvmanswer>$displaynum) || !is_numeric($kvmanswer) ) {
$kvmanswer = readline("Please choose a KVM guest (1-$displaynum): ");
}
echo "choice entered: $kvmanswer\n";
// because humans start at 1 and computers start at 0
$kvmchoiceminus = $kvmanswer - 1;
// we should not confuse kvm guest name with kvmadaptername
// we hardcode the name of the kvm adapter as the string "virbr0"
$kvmadaptername = "virbr0";
$kvmadapterip = $kvmipstrarr[$kvmchoiceminus];
echo "\n";
echo "kvmadaptername: $kvmadaptername\n";
echo "kvmadapterip: $kvmadapterip\n";
echo "\n";
// end ask client to choose KVM guest
// //////////////////////////////////////////////////////////////////////////////////
// start engine section
// construct the string variable containing the contents of the script file
$timestring = date("Y/m/d H:i:s T");
// start from nothing
$scriptcontents = "";
$scriptcontents .= "#!/usr/bin/bash\n";
$scriptcontents .= "# generated $timestring by forwardportstoguestgenerator.php v0102\n";
$scriptcontents .= "# Gordon Buchan https://gordonbuchan.com\n";
$scriptcontents .= "\n";
$scriptcontents .= "# values\n";
$scriptcontents .= "kvmsubnet=\"$kvmsubnet\"\n";
$scriptcontents .= "wanadaptername=\"$wanadaptername\"\n";
$scriptcontents .= "wanadapterip=\"$wanadapterip\"\n";
$scriptcontents .= "kvmadaptername=\"$kvmadaptername\"\n";
$scriptcontents .= "kvmadapterip=\"$kvmadapterip\"\n";
$scriptcontents .= "\n";
$scriptcontents .= "# allow virtual adapter to accept packets from outside the host\n";
$scriptcontents .= "iptables -I FORWARD -i \$wanadaptername -o \$kvmadaptername -d \$kvmsubnet -j ACCEPT\n";
$scriptcontents .= "iptables -I FORWARD -i \$kvmadapterip -o \$wanadaptername -s \$kvmsubnet -j ACCEPT\n";
$scriptcontents .= "# forward ports from host to guest\n";
$scriptcontents .= "iptables -t nat -A PREROUTING -i \$wanadaptername -d \$wanadapterip -p tcp --dport 80 -j DNAT --to-destination \$kvmadapterip:80\n";
$scriptcontents .= "iptables -t nat -A PREROUTING -i \$wanadaptername -d \$wanadapterip -p tcp --dport 443 -j DNAT --to-destination \$kvmadapterip:443\n";
$scriptcontents .= "iptables -t nat -A PREROUTING -i \$wanadaptername -d \$wanadapterip -p tcp --dport 8022 -j DNAT --to-destination \$kvmadapterip:22\n";
$scriptfilename = "forwardportstoguestscript.sh";
# write the text file
$fh = fopen("$scriptfilename","w");
$filesuccess = fwrite($fh,$scriptcontents);
fclose($fh);
if ($filesuccess) {
echo "SUCCESS script written to file: $scriptfilename\n";
chmod("$scriptfilename", 0755);
$scriptperms = substr(sprintf('%o', fileperms("$scriptfilename")), -4);
echo "scriptperms: $scriptperms\n";
if ($scriptperms == "0755") {
echo "SUCCESS chmod 755 $scriptfilename successful.\n";
} else {
echo "ERROR chmod 755 not $scriptfilename not successful.\n";
}
} else {
echo "ERROR script not written to file: $scriptfilename\n";
}
// end engine section
// /////////////////////
forwardportstoguestgenerator.php