Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                div
  section class="hero is-primary"
    div class="hero-body"
      div class="container"
        h1 class="title"
          | Ubiquity Security Gateway Multi WAN IP Configurator
        h2 class="subtitle"
          | Because theres no official tool / gui implementation
  section class="section"
    div class="tile is-ancestor"
      div class="tile is-vertical is-8"
        div class="tile"
          div class="tile is-parent is-vertical"
            article class="tile is-child box"
              div class="content"
                h2 Port Configurations
                p On USG3P the defaults are correct, for pro versions your WAN port may be eth2, and LAN1 on eth0. If you use PPPoE the WAN port may be pppoe0
              div class="field"
                label class="label"
                  | WAN Port
                div class="control"
                  input class="input usgc-wan-port" type="text" value="eth0"
              div class="field"
                label class="label"
                  | LAN Port
                div class="control"
                  input class="input usgc-lan-port" type="text" value="eth1"
            article class="tile is-child box usgc-ips"
              div class="content"
                h2 WAN Address configurations
                | new fields added automatically
                | <br><br>
                | Remeber to add all addresses including the USG's lan address if using as a cascaded router.
              div class="field usgc-default-ip"
                label class="label"
                  | Address with /subnet
                div class="control"
                  input class="input" type="text" placeholder="Must have /subnet"
          div class="tile is-parent"
            article class="tile is-child box usgc-configs"
              div class="content"
                h2 Rules
              a class="button usgc-new-config"
                | Add Rule
              div class="message-body card message usgc-default-config"
                div class="message-header"
                  | Rule
                  a class="delete"
                div class="card-content message-body"
                  div class="content"
                    div class="field"
                      label class="label"
                        | Rule Description
                      div class="control"
                        input class="input usgc-rulename" type="text" placeholder="What will this rule do?"
                    div class="field"
                      label class="label"
                        | WAN IP
                      div class="field has-addons"
                        div class="control"
                          div class="select"
                            select class="usgc-wanip"
                        div class="control"
                          a class="button is-warning" disabled=true
                            div class="css-tooltip"
                              | ?
                              span class="css-tooltip-text"
                                | This is the WAN address you want to forward traffic from<br>
                           
                    div class="field"
                      label class="label"
                        | WAN port
                      div class="field has-addons"
                        div class="control is-expanded"
                          input class="input usgc-wanport" type="text" placeholder="Incomming port(s)"
                        div class="control"
                          a class="button is-warning" disabled=true
                            div class='css-tooltip'
                              | ?
                              span class='css-tooltip-text'
                                | This is the incomming port from the wan.<br>
                                | You can set several ports in a comma seperated list<br>
                                | EG: 80,443
                    div class="field"
                      label class="label"
                        | LAN Target
                      div class="field has-addons"
                        div class="control is-expanded"
                          input class="input usgc-lanip" type="text" placeholder="internal machine ip"
                        div class="control"
                          a class="button is-warning" disabled=true
                            div class="css-tooltip"
                              | ?
                              span class="css-tooltip-text"
                                | This is the IP address of the LAN computer<br>
                                | that you want to redirect WAN traffic to.
                    div class="field"
                        label class="label"
                          | LAN port
                        div class="field has-addons"
                          div class="control is-expanded"
                            input class="input usgc-lanport" type="text" placeholder="Internal port(s)"
                          div class="control"
                            a class="button is-warning" disabled=true
                              div class='css-tooltip'
                                | ?
                                span class='css-tooltip-text'
                                  | Only use if one WAN port is set<br>
                                  | This will map the ONE incomming wan port to a different port on LAN<br>
                                  | If you use this with a range of ports the config will not be valid
                    div class="field"
                      label class="label"
                        | protocol
                      div class="control"
                        div class="select"
                          select class="usgc-protocol"
                            option value="tcp" selected=true tcp
                            option value="udp" udp
                            option value="tcp_udp" tcp_udp
                            option value="all" all
                    div class="field"
                      label class="checkbox"
                        input type="checkbox" checked=true class="usgc-build-dnat"
                        | Create DNAT Rule (recommended)
                        a class="button is-warning right" disabled=true
                          div class="css-tooltip"
                            | Whats This?
                            span class="css-tooltip-text tagged"
                              | This rule routes traffic from WAN to your server<br>
                              | Without this the gateway will not forward packets<br>
                              | Normally this should be on
                    div class="field"
                      label class="checkbox"
                        input type="checkbox" checked=true class="usgc-build-snat"
                        | Create SNAT Rule (recommended)
                        a class="button is-warning right" disabled=true
                          div class="css-tooltip"
                            | Whats This?
                            span class="css-tooltip-text tagged"
                              | This rule routes traffic from your server out via the set WAN IP<br>
                              | Without this the gateway may respond with the wrong WAN IP<br>
                              | Without the SNAT route many exposed services may not function.<br>
                              | This rule is not port aware so all WAN usage from listed interal ips<br>
                              | would appear to be coming from this WAN ip.
                    div class="field"
                      label class="checkbox"
                        input type="checkbox" checked=true class="usgc-build-firewall"
                        | Create Firewall WAN_IN Rule (recommended)
                        a class="button is-warning right" disabled=true
                          div class="css-tooltip"
                            | Whats This?
                            span class="css-tooltip-text tagged"
                              | This will cause the gateway to allow connections through<br>
                              | Without this your gateway may block connections<br>
                              | You need this if the firewall is enabled
                    div class="field"
                      label class="checkbox"
                        input type="checkbox" checked=true class="usgc-build-hairpin"
                        | Create Hairpin DNAT Rule (recommended)
                        a class="button is-warning right" disabled=true
                          div class="css-tooltip"
                            | Whats This?
                            span class="css-tooltip-text tagged"
                              | This will cause the WAN IPs to be usable within the LAN<br>
                              | Without this the WAN IPs may be unusable from LAN<br>
                              | This may need additional setup see footer for details
                    
                      
                                
                                
        div class="tile is-parent is-vertical"
          article class="tile is-child box message"
            div class="content"
              h2 Configuration JSON
            div class="message-header"
              | JSON Output
              button class="button usgc-gen-json"
                | Generate Config
            div class="message-body"
              div class='content'
                pre class="usgc-output-json"
          article class="title is-child box message"
            div class="content"
              h2 Input JSON
              p This is only designed to read configs created by this tool. It relies on the rule naming conventions that this tool uses! Check the accuracy of the gnerated rules, and all other settings for errors after importing json. This will remove any unnamed rules above, to avoid losing rules make sure they all have names before importing!
            div class="message-header"
              | Json Input
              button class="button usgc-get-json"
                | Read Config
            div class="message-body"
              div class="content"
                div class="field"
                  div class="control"
                    textarea class="textarea usgc-input-json"
      div class="tile is-parent"
        article class="tile is-child box"
          div class="content"
            h2 Other settings
          div class="field"
            label class="checkbox"
              input type="checkbox" class="usgc-hairpin-lanplus"
              | use LAN+ for hairpin nat (eg eth0+)
          div class="field"
            label class="checkbox"
              input type="checkbox" checked=true class="usgc-logs-firewalls"
              | log firewall rules
          div class="field"
            label class="label"
              | Firewall Rule Starting Number
            div class="control"
              input type="number" value="2000" class="input usgc-firewall-rule-num"
          div class="field"
            label class="label"
              | Firewall Rule Stride Length
            div class="control"
              input type="number" value="10" class="input usgc-firewall-rule-stride"
          div class="field"
            label class="label"
              | DNAT Rule Starting Number
            div class="control"
              input type="number" value="1000" class="input usgc-dnat-rule-num"
          div class="field"
            label class="label"
              | DNAT Rule Stride Length
            div class="control"
              input type="number" value="10" class="input usgc-dnat-rule-stride"
          div class="field"
            label class="label"
              | SNAT Rule Starting Number
            div class="control"
              input type="number" value="5000" class="input usgc-snat-rule-num"
          div class="field"
            label class="label"
              | SNAT Rule Stride Length
            div class="control"
              input type="number" value="10" class="input usgc-snat-rule-stride"
          div class="field"
            label class="label"
              | Hairpin Rule Starting Number
            div class="control"
              input type="number" value="1500" class="input usgc-hpin-rule-num"
          div class="field"
            label class="label"
              | Hairpin Rule Stride Length
            div class="control"
              input type="number" value="10" class="input usgc-hpin-rule-stride"
          div class="field"
            label class="label"
              | PortForward Rule Starting Number
            div class="control"
              input type="number" value="2000" class="input usgc-pf-rule-num"
          div class="field"
            label class="label"
              | PortForward Rule Stride Length
            div class="control"
              input type="number" value="10" class="input usgc-pf-rule-stride"
          div class="field"
            label class="checkbox"
              input type="checkbox" class="usgc-static-gateway"
              | Include Static Route to Gateway
          div class="field"
            label class="label"
              | Gateway IP
            div class="control"
              input type="text" class="input usgc-static-gateway-ip"
              
            
!

CSS

              
                .css-tooltip {
  position: relative;
  display: inline-block;
}

.css-tooltip .css-tooltip-text {
    visibility: hidden;
    width: 620px;
    background-color: black;
    color: #fff;
    text-align: center;
    padding: 5px 0;
    border-radius: 6px;
 
    /* Position the tooltip text - see examples below! */
    position: absolute;
    left: 0%;
    margin-left: -500px;
    bottom: 150%; 
    z-index: 1;
}

.css-tooltip .css-tooltip-text::after {
    content: " ";
    position: absolute;
    top: 100%; /* At the bottom of the tooltip */
    left: 50%;
    margin-left: 190px;
    border-width: 5px;
    border-style: solid;
    border-color: black transparent transparent transparent;
}

.css-tooltip:hover .css-tooltip-text {
    visibility: visible;
}

.right {
  float: right;
}
              
            
!

JS

              
                $ () ->
  regex_ip_with_subnet = /^(\d+\.\d+\.\d+\.\d+)\/\d+$/
  usgc_default_ip = $('.usgc-default-ip')
  usgc_default_ip.remove()
  usgc_ip_box = $('.usgc-ips')
  
  usgc_default_config = $('.usgc-default-config')
  usgc_default_config.remove()
  usgc_config_box = $('.usgc-configs')
    
  add_ip_box = (ip_value = "") ->
    console.log(ip_value)
    clone = usgc_default_ip.clone().appendTo(usgc_ip_box)
    clone.removeClass('usgc-default-ip')
    clone.addClass('usgc-ip').addClass('usgc-empty')
    input = clone.find('input')
    input.val(ip_value)
    input.on 'keyup', () ->
      ip = $(this).val()
      block = $(this).parents('.usgc-ip')
      
      
      #store ip when valid in data
      #when changed remove old ip from all configs
      #all changes even vald change (a valid change will have it re-added via event)
      if ip.match(regex_ip_with_subnet)
        pure_ip = regex_ip_with_subnet.exec($(this).val())[1]
        if $(this).data('ip') && $(this).data('ip') != pure_ip
          $(".usgc-wanip option[value=\"#{$(this).data('ip')}\"]").remove()
        $(this).data('ip',pure_ip)
        block.addClass('usgc-valid-ip')
        $(this).removeClass('is-danger')
        $(this).addClass('is-success')
        unless block.hasClass('usgc-ip-added')
          block.addClass('usgc-ip-added')
          add_ip_box()
      else
        usgc_ip_box.find('.usgc-empty').not(block).remove()
        block.removeClass('usgc-ip-added')
        block.removeClass('usgc-valid-ip')
        $(this).removeClass('is-success')
        $(this).addClass('is-danger')
      
      if ip #Better than searching
        block.removeClass('usgc-empty')
      else
        block.addClass('usgc-empty')
        block.removeClass('usgc-valid-ip')
        $(this).removeClass('is-success')
        $(this).removeClass('is-danger')
      
      $(".usgc-config").trigger('ip_update')
    input.trigger 'keyup' if ip_value
        
  add_ip_box()
  
  add_config_box = () ->
    clone = usgc_default_config.clone().appendTo(usgc_config_box)
    clone.removeClass('usgc-default-config')
    clone.addClass('usgc-config')
    clone.find('.delete').on "click", () ->
      clone.remove()
    clone.on "ip_update", () ->
      usgc_ip_box.find(".usgc-valid-ip input").each () ->
        ip = regex_ip_with_subnet.exec($(this).val())[1]
        unless clone.find(".usgc-wanip option[value=\"#{ip}\"]").length > 0
          ip_opt = $("<option value=\"#{ip}\">#{ip}</option>")
          clone.find('.usgc-wanip').append(ip_opt) #Only Add if not already here
    clone.trigger('ip_update')
    return clone
    
  $('.usgc-new-config').on "click", add_config_box
  add_config_box()
  
  prune_config_boxes = () ->
    $('.usgc-rulename').filter( () -> $(this).val() == "" ).closest('.usgc-config').remove()
  
  get_rule = (name) -> #this is bad practice, fix this later
    {
      name: name,
      wanip: null,
      wanport: null,
      lanip: null,
      lanport: null,
      protocol: null,
      dnat: false,
      snat: false,
      firewall: false,
      hairpin: false
    }
  
  read_nat_rule = (rules, rule) -> #Mutates rules instead of returning!!!
    rule_desc = /^(.+) for (.+)/.exec(rule.description)
    type = rule_desc[1]
    name = rule_desc[2]
    rules[name] ||= get_rule(name)
    if type.match(/SNAT/)
      rules[name].snat = true
      rules[name].lanip ||= rule.source.address
      rules[name].wanip ||= rule['outside-address'].address
      rules[name].protocol || rule.protocol
    if type.match(/Hairpin|DNAT/)
      rules[name].hairpin = true if type.match(/Hairpin/)
      rules[name].dnat = true if type.match(/DNAT/)
      rules[name].lanip ||= rule['inside-address'].address
      rules[name].wanip ||= rule.destination.address
      rules[name].wanport ||= rule.destination.port
      rules[name].lanport ||= rule['inside-address'].port
      rules[name].protocol ||= rule.protocol
  
  read_fw_rule = (rules, rule) ->
    if rule.description.match(/Firewall for .+/)
      name = /Firewall for (.+)/.exec(rule.description)[1]
      rules[name].firewall = true
      rules[name].lanip ||= rule.destination.address
      rules[name].protocol ||= rule.protocol
      
  
  read_config = () ->
    console.log('test')
    config = JSON.parse($('.usgc-input-json').val())
    wan_name = Object.keys(config.interfaces.ethernet)[0]
    $('.usgc-wan-port').val(wan_name)
    add_ip_box ip for ip in config.interfaces.ethernet[wan_name].address
    $('.usgc-ip input').trigger 'keyup'
    #get gateway stuff if it exists
    if config?.protocols?.static?.route['0.0.0.0/0']['next-hop']?
      gateway_ip = Object.keys(config.protocols.static.route['0.0.0.0/0']['next-hop'])[0]
      $('.usgc-static-gateway-ip').val(gateway_ip)
      $('.usgc-static-gateway').prop('checked', true)
    rules = {}
    read_nat_rule rules, rule for rule in Object.values(config.service.nat.rule)
    read_fw_rule rules, rule for rule in Object.values(config.firewall.name['WAN_IN'].rule)
    for name,rule of rules
      config_box = add_config_box()
      config_box.find('.usgc-rulename').val(name)
      config_box.find(".usgc-wanip option[value=\"#{rule.wanip}\"]").prop('selected', true)
      config_box.find('.usgc-wanport').val(rule.wanport)
      config_box.find('.usgc-lanip').val(rule.lanip)
      config_box.find('.usgc-lanport').val(rule.lanport)
      config_box.find(".usgc-protocol option[value=\"#{rule.protocol}\"]").prop('selected', true)
      config_box.find('.usgc-build-dnat').prop('checked', rule.dnat)
      config_box.find('.usgc-build-snat').prop('checked', rule.snat)
      config_box.find('.usgc-build-firewall').prop('checked', rule.firewall)
      config_box.find('.usgc-build-hairpin').prop('checked', rule.hairpin)
    prune_config_boxes()
    
  $('.usgc-get-json').on 'click', read_config
  
  create_config = () ->
    #other config variabels
    #get interface names
    wan_name = $('.usgc-wan-port').val()
    lan_name = $('.usgc-lan-port').val()
    full_lan_name = lan_name
    full_lan_name += '+' if $('.usgc-hairpin-lanplus').is(':checked')
    #get IPs
    ips_obj = $('.usgc-valid-ip input').map () ->
      $(this).val()
    ips = ips_obj.toArray()
    #get rules
    rules = $('.usgc-config').map () ->
      {
        rulename: $(this).find('.usgc-rulename').val(),
        wanip: $(this).find('.usgc-wanip').val(),
        wanport: $(this).find('.usgc-wanport').val(),
        lanip: $(this).find('.usgc-lanip').val(),
        lanport: $(this).find('.usgc-lanport').val(),
        protocol: $(this).find('.usgc-protocol option:selected').val(),
        build_dnat: $(this).find('.usgc-build-dnat').is(':checked'),
        build_snat: $(this).find('.usgc-build-snat').is(':checked'),
        build_firewall: $(this).find('.usgc-build-firewall').is(':checked'),
        build_hairpin: $(this).find('.usgc-build-hairpin').is(':checked')
      }
    # Buld blank config
    config = {
      interfaces: { ethernet: {} },
      service: { nat: { rule: {} } },
      firewall: { name: { WAN_IN: { rule: {} } } },
      'port-forward': {
        'hairpin-nat': "enable",
        'lan-interface': [
          lan_name
        ],
        'wan-interface': wan_name,
        rule: {}
      } }
    # Insert WAN ips on WAN interface
    config.interfaces.ethernet[wan_name] = {
      address: ips
    }
    # first create rule number counters
    # These should be exposed on UI # TODO
    firewall_rule_num = parseInt($('.usgc-firewall-rule-num').val()) #stating number
    firewall_rule_stride = parseInt($('.usgc-firewall-rule-stride').val()) #how much to move up each rule
    dnat_rule_num = parseInt($('.usgc-dnat-rule-num').val())
    dnat_rule_stride = parseInt($('.usgc-dnat-rule-stride').val())
    snat_rule_num = parseInt($('.usgc-snat-rule-num').val())
    snat_rule_stride = parseInt($('.usgc-snat-rule-stride').val())
    hpin_rule_num = parseInt($('.usgc-hpin-rule-num').val()) #for hairpin snat
    hpin_rule_stride = parseInt($('.usgc-hpin-rule-stride').val())
    pf_rule_num = parseInt($('.usgc-pf-rule-num').val()) #for hairpin snat
    pf_rule_stride = parseInt($('.usgc-pf-rule-stride').val())
    # Insert rules
    for rule in rules
      next unless rule.rulename
      if rule.build_firewall
        #build firewall
        fwrule = {
          action: 'accept',
          description: "Firewall for #{rule.rulename}",
          destination: {
            address: rule.lanip,
            port: if rule.lanport == "" then rule.wanport else rule.lanport
          },
          protocol: rule.protocol,
          log: "enable" if $('.usgc-logs-firewalls').is(':checked')
        }
        config.firewall.name.WAN_IN.rule[firewall_rule_num] = fwrule
        firewall_rule_num += firewall_rule_stride
      
      if rule.build_dnat
        #build dnat
        dnatrule = {
          description: "DNAT for #{rule.rulename}",
          destination: {
            address: rule.wanip,
            port: rule.wanport
          },
          'inbound-interface': wan_name,
          'inside-address': {
            address: rule.lanip,
            port: rule.lanport unless rule.lanport == ""
          },
          protocol: rule.protocol,
          type: "destination"
        }
        config.service.nat.rule[dnat_rule_num] = dnatrule
        dnat_rule_num += dnat_rule_stride
    
      if rule.build_snat
        #build snat
        snatrule = {
          description: "SNAT for #{rule.rulename}",
          'outbound-interface': wan_name,
          'outside-address': {
            address: rule.wanip
          },
          protocol: rule.protocol,
          source: {
            address: rule.lanip
          },
          type: "source"
        }
        config.service.nat.rule[snat_rule_num] = snatrule
        snat_rule_num += snat_rule_stride
      
      if rule.build_hairpin
        #build hairpin
        hpinrule = {
          description: "Hairpin for #{rule.rulename}",
          destination: {
            address: rule.wanip,
            port: rule.wanport
          },
          'inbound-interface': full_lan_name,
          'inside-address': {
            address: rule.lanip,
            port: rule.lanport unless rule.lanport == ""
          },
          protocol: rule.protocol,
          type: "destination"
        }
        config.service.nat.rule[hpin_rule_num] = hpinrule
        hpin_rule_num += hpin_rule_stride
        
        hpin_fw_rule = {
          action: "accept",
          description: "Hairpin FW for #{rule.rulename}",
          destination: {
            address: rule.lanip,
            port: if rule.lanport == "" then rule.wanport else rule.lanport
          },
          protocol: rule.protocol,
          source: {
            address: rule.wanip
          },
          log: "enable" if $('.usgc-logs-firewalls').is(':checked')
        }
        config.firewall.name.WAN_IN.rule[firewall_rule_num] = hpin_fw_rule
        firewall_rule_num += firewall_rule_stride
        
        hpin_pfwd_rule = {
          description: "Hairpin Portfoward for #{rule.rulename}",
          'forward-to': {
            address: rule.lanip,
            port: rule.lanport unless rule.lanport == ""
          },
          protocol: rule.protocol,
          'original-port': rule.wanport
        }
        config['port-forward'].rule[pf_rule_num] = hpin_pfwd_rule
        pf_rule_num += pf_rule_stride
        
    
    if $('.usgc-static-gateway').is(':checked')
      gateway_route = {
        static: {
          route: {
            '0.0.0.0/0': {
              'next-hop': {
                "#{$('.usgc-static-gateway-ip').val()}": {
                  distance: 1
                }
              }
            }
          }
        }
      }
      config['protocols'] = gateway_route
    
    config
  
  $('.usgc-gen-json').on 'click', () ->
    config = create_config()
    config_string = JSON.stringify(config, null, 2)
    $('.usgc-output-json').text(config_string)
              
            
!
999px

Console