package jfconfig; /** * Created : Mar 14, 2012 * * @author pquiring */ import java.io.*; import java.util.*; import javax.swing.*; import javaforce.*; import javaforce.linux.*; public class FirewallPanel extends javax.swing.JPanel { /** * Creates new form FirewallPanel */ public FirewallPanel() { initComponents(); loadRules(); updateRules(); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jToolBar1 = new javax.swing.JToolBar(); back = new javax.swing.JButton(); addRule = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); jButton3 = new javax.swing.JButton(); jButton4 = new javax.swing.JButton(); jButton5 = new javax.swing.JButton(); preview = new javax.swing.JButton(); up = new javax.swing.JButton(); down = new javax.swing.JButton(); jScrollPane1 = new javax.swing.JScrollPane(); rules = new javax.swing.JList(); basicFirewall = new javax.swing.JCheckBox(); jToolBar1.setFloatable(false); jToolBar1.setRollover(true); back.setText("<Back"); back.setFocusable(false); back.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); back.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); back.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { backActionPerformed(evt); } }); jToolBar1.add(back); addRule.setText("Add Rule"); addRule.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { addRuleActionPerformed(evt); } }); jToolBar1.add(addRule); jButton2.setText("Delete Rule"); jButton2.setFocusable(false); jButton2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); jButton2.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); jButton2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButton2ActionPerformed(evt); } }); jToolBar1.add(jButton2); jButton3.setText("Edit Rule"); jButton3.setFocusable(false); jButton3.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); jButton3.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); jButton3.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButton3ActionPerformed(evt); } }); jToolBar1.add(jButton3); jButton4.setText("Save"); jButton4.setFocusable(false); jButton4.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); jButton4.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); jButton4.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButton4ActionPerformed(evt); } }); jToolBar1.add(jButton4); jButton5.setText("Apply"); jButton5.setFocusable(false); jButton5.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); jButton5.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); jButton5.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButton5ActionPerformed(evt); } }); jToolBar1.add(jButton5); preview.setText("Preview"); preview.setFocusable(false); preview.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); preview.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); preview.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { previewActionPerformed(evt); } }); jToolBar1.add(preview); up.setText("Up"); up.setFocusable(false); up.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); up.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); up.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { upActionPerformed(evt); } }); jToolBar1.add(up); down.setText("Down"); down.setFocusable(false); down.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); down.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); down.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { downActionPerformed(evt); } }); jToolBar1.add(down); rules.setModel(rulesModel); rules.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); jScrollPane1.setViewportView(rules); basicFirewall.setText("Enable Basic Firewall Protection (block all inbound traffic to localhost)"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 587, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane1) .addComponent(basicFirewall, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(basicFirewall) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 549, Short.MAX_VALUE) .addContainerGap()) ); }// </editor-fold>//GEN-END:initComponents private void backActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backActionPerformed ConfigApp.setPanel(new NetworkPanel()); }//GEN-LAST:event_backActionPerformed private void addRuleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addRuleActionPerformed addRule(); }//GEN-LAST:event_addRuleActionPerformed private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed int idx = rules.getSelectedIndex(); if (idx == -1) return; delRule(idx); }//GEN-LAST:event_jButton2ActionPerformed private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed int idx = rules.getSelectedIndex(); if (idx == -1) return; editRule(idx); }//GEN-LAST:event_jButton3ActionPerformed private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed saveRules(); }//GEN-LAST:event_jButton4ActionPerformed private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed applyRules(); }//GEN-LAST:event_jButton5ActionPerformed private void previewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_previewActionPerformed String str = previewRules(); JF.showMessage("Preview", str); }//GEN-LAST:event_previewActionPerformed private void upActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_upActionPerformed int idx = rules.getSelectedIndex(); if (idx == -1) return; up(idx); }//GEN-LAST:event_upActionPerformed private void downActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_downActionPerformed int idx = rules.getSelectedIndex(); if (idx == -1) return; down(idx); }//GEN-LAST:event_downActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton addRule; private javax.swing.JButton back; private javax.swing.JCheckBox basicFirewall; private javax.swing.JButton down; private javax.swing.JButton jButton2; private javax.swing.JButton jButton3; private javax.swing.JButton jButton4; private javax.swing.JButton jButton5; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JToolBar jToolBar1; private javax.swing.JButton preview; private javax.swing.JList rules; private javax.swing.JButton up; // End of variables declaration//GEN-END:variables DefaultListModel rulesModel = new DefaultListModel(); public static class Rule { public boolean enabled; public String name; public int type; //see FirewallRuleDialog.java public String opts; } public static class Config { public Rule rule[]; public boolean basicFirewall; } private Config config; private String configFolder = "/etc/jconfig.d/"; private String configFile = "firewall.xml"; private void loadRules() { config = new Config(); config.rule = new Rule[0]; try { XML xml = new XML(); FileInputStream fis = new FileInputStream(configFolder + configFile); xml.read(fis); xml.writeClass(config); } catch (FileNotFoundException e1) { defaultConfig(); } catch (Exception e2) { JFLog.log(e2); defaultConfig(); } basicFirewall.setSelected(config.basicFirewall); } private void saveRules() { config.basicFirewall = basicFirewall.isSelected(); try { XML xml = new XML(); File tmpFile = File.createTempFile("firewall", ".xml"); FileOutputStream fos = new FileOutputStream(tmpFile); xml.readClass("firewall", config); xml.write(fos); fos.close(); Linux.mkdir(configFolder); if (!Linux.copyFile(tmpFile.getAbsolutePath(), configFolder + configFile)) { tmpFile.delete(); throw new Exception("file io error"); } tmpFile.delete(); } catch (Exception e) { JFLog.log(e); } } private void defaultConfig() { config = new Config(); config.rule = new Rule[0]; } private void up(int idx) { if (idx == 0) return; Rule tmp = config.rule[idx-1]; config.rule[idx-1] = config.rule[idx]; config.rule[idx] = tmp; updateRules(); rules.setSelectedIndex(idx-1); } private void down(int idx) { if (idx == config.rule.length-1) return; Rule tmp = config.rule[idx+1]; config.rule[idx+1] = config.rule[idx]; config.rule[idx] = tmp; updateRules(); rules.setSelectedIndex(idx+1); } private String getRuleString(Rule rule) { String ret = ""; switch (rule.type) { case 0: ret += "(exc)"; break; case 1: ret += "(nat)"; break; case 2: ret += "(pf)"; break; case 3: ret += "(dmz)"; break; } ret += " "; ret += rule.name; return ret; } private void updateRules() { rulesModel.clear(); for(int a=0;a<config.rule.length;a++) { rulesModel.addElement(getRuleString(config.rule[a])); } } private void addRule() { FirewallRuleDialog dialog = new FirewallRuleDialog(ConfigApp.This, true, null, "rule-" + (config.rule.length+1)); dialog.setVisible(true); if (!dialog.accepted) return; Rule newRule = new Rule(); newRule.name = dialog.getName(); newRule.enabled = dialog.getEnabled(); newRule.type = dialog.getRuleType(); newRule.opts = dialog.getOpts(); config.rule = Arrays.copyOf(config.rule, config.rule.length + 1); config.rule[config.rule.length-1] = newRule; updateRules(); } private void delRule(int idx) { Rule rule = config.rule[idx]; if (!JF.showConfirm("Confirm", "Are you sure you want to delete '" + rule.name + "'?")) return; int len = config.rule.length; Rule newList[] = new Rule[len-1]; System.arraycopy(config.rule, 0, newList, 0, idx); System.arraycopy(config.rule, idx+1, newList, idx, len - idx - 1); config.rule = newList; updateRules(); } private void editRule(int idx) { Rule rule = config.rule[idx]; FirewallRuleDialog dialog = new FirewallRuleDialog(ConfigApp.This, true, rule, null); dialog.setVisible(true); if (!dialog.accepted) return; rule.name = dialog.getName(); rule.enabled = dialog.getEnabled(); rule.type = dialog.getRuleType(); rule.opts = dialog.getOpts(); updateRules(); } private void applyRules() { //save previewRules() to /etc/jconfig.d/firewall.sh try { File tmpFile = File.createTempFile("firewall", ".sh"); FileOutputStream fos = new FileOutputStream(tmpFile); fos.write("#!/bin/bash\n".getBytes()); fos.write("iptables -F\n".getBytes()); String rules = previewRules(); fos.write(rules.getBytes()); fos.close(); if (!Linux.copyFile(tmpFile.getAbsolutePath(), "/etc/jconfig.d/firewall.sh")) { tmpFile.delete(); throw new Exception("file io error"); } if (!Linux.setExec("/etc/jconfig.d/firewall.sh")) { tmpFile.delete(); throw new Exception("file io error"); } tmpFile.delete(); } catch (Exception e) { JFLog.log(e); JF.showError("Error", "Failed to save rules to /etc/jconfig.d/firewall.sh"); return; } //exec /etc/jconfig.d/firewall.sh to apply now try { ShellProcess sp = new ShellProcess(); String cmd[] = {"sudo", "/etc/jconfig.d/firewall.sh"}; sp.run(cmd, false); } catch (Exception e) { JFLog.log(e); JF.showError("Error", "Failed to exec /etc/jconfig.d/firewall.sh"); return; } //add /etc/jconfig.d/firewall.sh to /etc/rc.local for next reboot try { FileInputStream fis = new FileInputStream("/etc/rc.local"); byte data[] = JF.readAll(fis); String str = new String(data); String lns[] = str.split("\n"); int exitIdx = -1; for(int a=0;a<lns.length;a++) { if (lns[a].startsWith("exit ")) exitIdx = a; if (lns[a].indexOf("/etc/jconfig.d/firewall.sh") != -1) return; //already done } File tmpFile = File.createTempFile("rc_local", ".tmp"); FileOutputStream fos = new FileOutputStream(tmpFile); for(int a=0;a<lns.length;a++) { if (a == exitIdx) { fos.write("/etc/jconfig.d/firewall.sh\n".getBytes()); } fos.write(lns[a].getBytes()); fos.write("\n".getBytes()); } if (exitIdx == -1) { fos.write("/etc/jconfig.d/firewall.sh\n".getBytes()); } fos.close(); if (!Linux.copyFile(tmpFile.getAbsolutePath(), "/etc/rc.local")) { tmpFile.delete(); throw new Exception("file io error"); } if (!Linux.setExec("/etc/rc.local")) { tmpFile.delete(); throw new Exception("file io error"); } tmpFile.delete(); } catch (Exception e) { JFLog.log(e); JF.showError("Error", "Failed to add firewall.sh to /etc/rc.local"); } } /* * iptables -F * iptables -A chain <spec> * chains : INPUT, FORWARD, OUTPUT * spec: -p <protocol:tcp,udp,icmp,all> * spec: -s <source/mask>[,...] * spec: -d <dest/mask>[,...] * spec: --sport <sourceport>[:<sourceportrange>] * spec: --dport <destport>[:<destportrange>] * * Config file : /etc/jconfig.d/firewall.sh (run from /etc/rc.local) */ private String getOpt(String opt, String opts) { //opts = "protocol=x;port=y;etc" String ol[] = opts.split(";"); for(int a=0;a<ol.length;a++) { String f[] = ol[a].split("="); if (f[0].equals(opt)) return f[1]; } return ""; } private String previewRules() { StringBuilder str = new StringBuilder(); boolean enable_ip_forwarding = false; boolean dmz = false, warned = false, nat = false; for(int a=0;a<config.rule.length;a++) { Rule rule = config.rule[a]; if (!rule.enabled) continue; str.append("#" + rule.name + "\n"); String opts = rule.opts; switch(rule.type) { case 0: //exception for localhost //iptables -A INPUT -p $PROTOCOL -s ! 127.0.0.1 --dport $PORT -j $ACTION str.append("iptables -A INPUT -p "); str.append(getOpt("protocol", opts)); str.append(" ! -s 127.0.0.1/32"); str.append(" --dport "); str.append(getOpt("port", opts)); str.append(" -j "); str.append(getOpt("action", opts)); str.append("\n"); break; case 1: //nat //iptables -t nat -A POSTROUTING -o $WAN -j MASQUERADE //iptables -A FORWARD -i $WAN -o $LAN -m state --state RELATED,ESTABLISHED -j ACCEPT //iptables -A FORWARD -i $LAN -o $WAN -j ACCEPT enable_ip_forwarding = true; if (nat) { JF.showError("Warning", "You have more than one NAT rule"); } nat = true; str.append("iptables -t nat -A POSTROUTING -o "); str.append(getOpt("wan", opts)); str.append(" -j MASQUERADE\n"); str.append("iptables -A FORWARD -i "); str.append(getOpt("wan", opts)); str.append(" -o "); str.append(getOpt("lan", opts)); str.append(" -m state --state RELATED,ESTABLISHED -j ACCEPT\n"); str.append("iptables -A FORWARD -i "); str.append(getOpt("lan", opts)); str.append(" -o "); str.append(getOpt("wan", opts)); str.append(" -j ACCEPT\n"); break; case 2: //port forwarding //iptables -t nat -A PREROUTING -p $PROTOCOL -i $WAN --dport $PORT -j DNAT --to $IP:$DPORT //iptables -A FORWARD -p $PROTOCOL -i $WAN -d $IP --dport $DPORT -j ACCEPT enable_ip_forwarding = true; if ((dmz) && (!warned)) { warned = true; JF.showError("Warning", "You have a port forwarding rule after your DMZ rule.\nYou must move the DMZ after all port forwarding rules, or they will not work."); } str.append("iptables -t nat -A PREROUTING -p tcp -i "); str.append(getOpt("wan", opts)); str.append(" --dport "); int port = JF.atoi(getOpt("port", opts)); str.append(port); int length = JF.atoi(getOpt("length", opts)); if (length > 1) { str.append(":"); str.append( (port + length - 1) ); } str.append(" -j DNAT --to "); str.append(getOpt("ip", opts)); str.append(":"); str.append(getOpt("dport", opts)); str.append("\n"); str.append("iptables -A FORWARD -p "); str.append(getOpt("protocol", opts)); str.append(" -i "); str.append(getOpt("wan", opts)); str.append(" -d "); str.append(getOpt("ip", opts)); str.append(" --dport "); str.append(getOpt("dport", opts)); str.append(" -j ACCEPT\n"); break; case 3: //dmz //DMZ should be compatible with NATing (there will be two MASQUERADEs -- not an issue) //iptables -t nat -A PREROUTING -i $WAN [-d $PUBLICIP] -j DNAT --to $PRIVATEIP //iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT //iptables -A FORWARD -i $WAN -d $PRIVATEIP -m state --state NEW -j ACCEPT //iptables -t nat -A POSTROUTING -o $WAN -j MASQUERADE enable_ip_forwarding = true; dmz = true; str.append("iptables -t nat -A PREROUTING -i "); str.append(getOpt("wan", opts)); if (getOpt("publicip", opts).length() > 0) { str.append(" -d "); str.append(getOpt("publicip", opts)); } str.append(" -j DNAT --to "); str.append(getOpt("privateip", opts)); str.append("\n"); str.append("iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\n"); str.append("iptables -A FORWARD -i "); str.append(getOpt("wan", opts)); str.append(" -d "); str.append(getOpt("privateip", opts)); str.append(" -m state --state NEW -j ACCEPT\n"); str.append("iptables -t nat -A POSTROUTING -o "); str.append(getOpt("wan", opts)); str.append(" -j MASQUERADE\n"); break; } } if (enable_ip_forwarding) { str.append("#enable IP forwarding\n"); str.append("echo 1 > /proc/sys/net/ipv4/ip_forward\n"); str.append("iptables -P FORWARD DROP\n"); } if (config.basicFirewall) { str.append("#basicFirewall rule\n"); str.append("iptables -A INPUT -s 127.0.0.1/32 -j ACCEPT\n"); //not sure why I need this? str.append("iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n"); str.append("iptables -A INPUT -m state --state NEW -j DROP\n"); } return str.toString(); } }