ruby
23 TopicsRuby NAT Maker
Problem this snippet solves: The purpose of the NAT Maker tool is to add or delete a list of NATs. The file bigip-config.ylm contains the information about the BIG-IP that the tool is to connect to. It needs to be updated with the name and IP address of the BIG-IP as well as the username and password of the administrator account on the BIG-IP. The file LocalLB.NAT.WSDL defines the NAT section of iControl. This WSDL was used to generate some classes used this tool. It also needs to be updated. The 4th to last line in this file is < soap:address location="https://:443/iControl/iControlPortal.cgi"/> This line needs to be altered to use the IP address of the actual BIG-IP to which this script will connect. How to use this snippet: Tool Usage Add a list of NATs NATMaker.rb -a inputfile Delete a list of NATs NATMaker.rb -d inputfile Delete all NATs NATMaker.rb -dd Note: The input file should consist of a series of NATs, each on its own line. A NAT is defined by two IP addresses separated by a space. The first address is the origin address, and the second is the translation address. Code : # missing file185Views0likes0CommentsRuby VIP Maker
Problem this snippet solves: This is a utility created for the purpose of producing large numbers of virtual servers, including pools. This was created to make our testing efforts easier. Configuration For MakeVips and DeleteVips to work, the following files need the following edits: ltmbigip-config.yml needs to be updated with the correct IP address of the LTM and the username and password. LocalLB.Pool.wsdl needs to have the IP address on the second to last line of the file updated to the correct IP of the LTM LocalLB.VirtualServer.wsdl needs to have the IP address on the second to last line of the file updated to the correct IP of the LTM How to use this snippet: Tool Usage Add Virtual Servers MakeVips.rb namePrefix numberOfVIPs vipStartIP port ipsPerPool poolStartIP This will create numberOfVIPs virtual servers on the BIG-IP. These virtual servers will have names that begin with namePrefix. Each VIP will answer on its own IP address beginning with vipStartIP and counting up. The IP addresses roll over at their boundaries. Every VIP will answer on the specified port. Every VIP will have its own unique pool containing ipsPerPool number of IP addresses as members. These nodes begin at poolStartIP IP address and count up just as the VIP IP addresses do. There will be numberOfVips multiplied by ipsPerPool number of nodes created by this utility. Delete Virtual Servers DeleteVips.rb namePrefix numberOfVIPs This will delete the first numberOfVIPs virtual servers from the BIG-IP beginning with namePrefix, along with the pools associated with those virtual servers, assuming that they were created with the same utility as described above.287Views0likes0CommentsRuby Wide IP Maker
Problem this snippet solves: This is a utility created for the purpose of producing large numbers of WideIPs, including pools. This was created to make our testing efforts easier. Configuration For MakeWideIps and DeleteWideIps to work, the following files need the following edits: gtmbigip-config.yml needs to be updated with the correct IP address of the GTM and the username and password. GlobalLB.Pool.wsdl needs to have the IP address on the second to last line of the file updated to the correct IP of the GTM GlobalLB.WideIp.wsdl needs to have the IP address on the second to last line of the file updated to the correct IP of the GTM How to use this snippet: Tool Usage Add WideIPs MakeWideIps.rb namePrefix numberOfWideIPs port ipsPerPool poolStartIP This will create numberOfWideIPs WideIPs on the BIG-IP. These WideIPs will have names that begin with namePrefix. Every WideIP will answer on the specified port. Every WideIP will have its own unique pool containing ipsPerPool number of IP addresses as members. These nodes begin at poolStartIP IP address and count up . The IP addresses roll over at their boundaries. There will be numberOfWideIPs multiplied by ipsPerPool number of nodes created by this utility. Delete WideIPs DeleteWideIps.rb namePrefix numberOfWideIPs This will delete the first numberOfWideIPs WideIPs from the BIG-IP beginning with namePrefix, along with the pools associated with them, assuming that they were created with the same utility as described above.217Views0likes0CommentsRuby iControl throwing non-descript soap fault on NodeAddressV2.create
So I'm receiving a soap fault when I try and create a node. If anyone could help me out with a diagnosis I'd really appreciate it!! bigip = F5::IControl.new('*******', '*******', '*******', ['LocalLB.Pool', 'LocalLB.NodeAddressV2']) interfaces = bigip.get_interfaces interfaces['LocalLB.NodeAddressV2'].create ['test'], ['test.test.com'], [0] Throws the error: SOAP::FaultError: Exception caught in LocalLB::urn:iControl:LocalLB/NodeAddressV2::create() Common::InvalidArgument252Views0likes1CommentMitigating Ruby YAML.load Universal RCE Deserialization Gadget with BIG-IP ASM
Recently a new YAML deserialization gadget was published that may lead to arbitrary code execution when deserialized by supplying it to Ruby'sYAML.load function as input. Prior to this new gadget it was known that calling YAML.load on user supplied input is a bad idea, but this was only relevant to applications that are based on the Ruby on Rails web framework. In this case the published gadget is universal, meaning it will only depend on the existence of classes that are shipped with the default installation of Ruby, and thus it will be able to execute code in any Ruby application that deserializes YAML documents using the YAML.load function. Mitigating the vulnerability with BIG-IP ASM BIG-IP ASM customers under any supported BIG-IP version are already protected against this vulnerability. The exploitation attempt will be detected by existing Ruby code injection attack signatures which can be found in signature sets that include the “Server Side Code Injection” attack type or “Ruby” System. Figure 1: Exploit blocked with attack signature 200004159 Additional Reading https://staaldraad.github.io/post/2019-03-02-universal-rce-ruby-yaml-load/764Views0likes0CommentsiControlREST: Ruby: To Output a List of Virtual Servers and their Associated Pools and Pool Members.
Problem this snippet solves: This script enumerates virtual servers and their associated pools and pool members and their status, each virtual server on a line of its own in the ";' delimited format. Essentially, the code shows a way to parse the 'ltm/virtual' object and get the information required. It had a specific need to address when it was written, and it can be modified to suit other similar requirements. How to use this snippet: This code has been tested with Ruby 2.3.1p112. Code : #!/usr/local/bin/ruby -w # # This script enumerates virtual servers and their associated pools and # pool members and their status, each virtual server on a line of its own # in the ";' delimited format. # #------------------------------------------------------------------------- require 'rubygems' require 'rest-client' require 'json' # define program-wide variables BIGIP_ADDRESS = 'mgmt_IP_addr' BIGIP_USER = 'admin' BIGIP_PASS = 'admin' SLEEP_TIME = 20 bigip = RestClient::Resource.new( "https://#{BIGIP_ADDRESS}/mgmt/tm/", :user => BIGIP_USER, :password => BIGIP_PASS, :headers => { :content_type => 'application/json' }, :verify_ssl => false ) # Get virtual servers: vservers = bigip['ltm/virtual'].get vservers_obj = JSON.parse(vservers) # Get pools: pools = bigip['ltm/pool?expandSubcollections=true'].get pools_obj = JSON.parse(pools) # Output header: puts "Virtual Server Name;Virtual Server Destination;Virtual Server Partition;Pool Name;Pool LB Mode;Pool Member Name (address)(state)" $output = '' # Process data: vservers_obj.each do |vserver_obj| vserver_obj.each do |vserver_obj_element| if vserver_obj_element.is_a?(Array) vserver_obj_element.each do |vserver_obj_element_property| if vserver_obj_element_property.is_a?(Hash) if vserver_obj_element_property.has_key?("name") vs_name = vserver_obj_element_property.fetch("name") $output = $output + vs_name + ';' end if vserver_obj_element_property.has_key?("destination") vs_destination = vserver_obj_element_property.fetch("destination") $output = $output + vs_destination + ';' end if vserver_obj_element_property.has_key?("partition") vs_partition = vserver_obj_element_property.fetch("partition") $output = $output + vs_partition + ';' end if vserver_obj_element_property.has_key?("pool") pool_name_from_vs = vserver_obj_element_property.fetch("pool") pools_obj.each_pair do |key, val| next if key == "kind" next if key == "selfLink" for x in 0..(val.length-1) if val[x]["fullPath"] == pool_name_from_vs $output = $output + val[x]["name"] + ";" + val[x]["loadBalancingMode"] if val[x].has_key?("membersReference") val[x]["membersReference"].each_pair do |mrefkey,mrefval| next if mrefkey == "link" next if mrefkey == "isSubcollection" for i in 0..(mrefval.length-1) if i == 0 $output = $output + ";" + mrefval[i]["name"] + "(" + mrefval[i]["address"] + ")(" + mrefval[i]["state"] + ")," else $output = $output + mrefval[i]["name"] + "(" + mrefval[i]["address"] + ")(" + mrefval[i]["state"] + ")," end if i == mrefval.length-1 $output.chop! $output += "\n" end end end else $output = $output + ";;\n" end break end end end else $output = $output + ";;\n" end end end end end end puts $output #---END--- Tested this on version: 11.6269Views0likes0CommentsEnumerate All Modules
Problem this snippet solves: The iControl REST Users Guide documents a way to discover all components by using the curl command to manually scroll through all the different modules. I wanted something a bit more automated, so I came up with this short program to enumerate through all the possible modules. The program loads in all the top-level API modules into a stack, then proceeds to query the iControl REST API daemon (tmapid) for the contents of those containers. As the sub-modules are discovered, they are pushed down on to the stack to be queried as well. Since the REST API makes use of the same command processor as TMSH, you will notice that the item REST paths just about match up with TMSH. If you know the TMSH path, you can just about guess what the REST URI should look like. Code : #!/usr/bin/ruby #---------------------------------------------------------------------------- # iControl_REST_Enum.rb #---------------------------------------------------------------------------- # # # Before you can run this program, you need to enable the tmapid daemon: #tmsh modify /sys service tmapid enable # tmsh start /sys service tmapid # # You can query the iControl REST API from the command line using curl: # # $ curl -s -k -u admin:admin https://10.147.29.117/mgmt/tm/sys/application | python -mjson.tool # { # "apiPartition": "/Common/", # "currentItemCount": 4, # "items": [ # { # "apiName": "apl-script", # "kind": "tm:sys:application:apl-script:apl-scriptstate", # "selfLink": "https://localhost/mgmt/tm/sys/application/apl-script" # }, # { # "apiName": "custom-stat", # "kind": "tm:sys:application:custom-stat:custom-statstate", # "selfLink": "https://localhost/mgmt/tm/sys/application/custom-stat" # }, # { # "apiName": "service", # "kind": "tm:sys:application:service:servicestate", # "selfLink": "https://localhost/mgmt/tm/sys/application/service" # }, # { # "apiName": "template", # "kind": "tm:sys:application:template:templatestate", # "selfLink": "https://localhost/mgmt/tm/sys/application/template" # } # ], # "kind": "tm:sys:application:applicationstate", # "nextLink": null, # "pageIndex": 1, # "previousLink": null, # "selfLink": "https://localhost/mgmt/tm/sys/application", # "startIndex": 1, # "totalItems": 4, # "totalPages": 1 # } # # The python json.tool on the end of the command line just formats the JSON into something # more readable. #---------------------------------------------------------------------------- # Software is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and limitations # under the License. # # The Initial Developer of the Original Code is F5 Networks, # Inc. Seattle, WA, USA. Portions created by F5 are Copyright (C) 2013 F5 Networks, # Inc. All Rights Reserved. # # Author: John D. Allen, Principal Solutions Engineer # Email: john.allen@f5.com #---------------------------------------------------------------------------- # # Version: #1.0INITIAL Version #1.1Corrected to work with new icrd daemon JSON format. # #---------------------------------------------------------------------------- require 'rubygems' require 'net/http' require 'net/https' require 'uri' require 'json' REST_host = "//10.147.29.121" ## Your BIGIP v11.4 host IP or Name here. The '//' at the start need to be there. def RESTcall(x) ## Yes there are other ways to do this call, but I already had the tried and true code in hand. uri = URI.parse(x) http = Net::HTTP.new(uri.host, uri.port) req = Net::HTTP::Get.new(uri.request_uri) req.basic_auth "admin", "admin" ## You will need to change this if you have a different password. http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE ## if your like me, you don't have a valid cert on your BIGIP return http.request(req) end ##----- Load the query stack -----## stack = [] stack.push "https:#{REST_host}/mgmt/tm/sys" stack.push "https:#{REST_host}/mgmt/tm/net" stack.push "https:#{REST_host}/mgmt/tm/ltm" stack.push "https:#{REST_host}/mgmt/tm/cm" stack.push "https:#{REST_host}/mgmt/tm/cli" stack.push "https:#{REST_host}/mgmt/tm/auth" stack.push "https:#{REST_host}/mgmt/tm/pem" stack.push "https:#{REST_host}/mgmt/tm/security" stack.push "https:#{REST_host}/mgmt/tm/afm" stack.push "https:#{REST_host}/mgmt/tm/analytics" stack.push "https:#{REST_host}/mgmt/tm/apm" stack.push "https:#{REST_host}/mgmt/tm/gtm" stack.push "https:#{REST_host}/mgmt/tm/wam" ## If you don't have a module installed, you will get a '500' error, but the program will continue. resp = "" ## ## Check our host to see if it is ready ## For some reason the initial REST API calls usually timeout while the tmapid daemon works on the ## first API packet. Once going, it seems to be fine. This call just checks to see if it is ## responding back with a correct '200' status code. If not, tmapid responds back with a '000' ## status code. We just loop until we get a 200, or die trying. ## i = 0 loop do i += 1 resp = RESTcall("https:#{REST_host}/mgmt/tm/ltm/available") break if resp.code == "200" or i > 5 puts "--> Waking up tmapid daemon" end if i > 5 puts "--->Unable to wake up tmapid daemon on BIGIP host." exit end ## ## Main Loop ## ## This program uses a simple FILO stack to enumerate through all the items exposed from the iControl REST API. ## Some containers have large numbers of items, so if there are more than 50, they are not pushed onto the stack. ## There are a few containers that seem to reference themselves, and thus were causing loops in the program. So ## if the selfLink field of the item matches the selfLink field of the container, the item is ignored. Some of ## the containers seem to have items defined that cause errors when queried for. Not sure if that is by design, ## or if it is a bug in the v11.4 Beta code. ## loop do uri = stack.pop uu = URI.parse(uri) puts uu.path uri.gsub!(/\/\/localhost/, REST_host) ## BIGIP REST interface returns 'localhost' in its URI's for some strange reason. i = 0 loop do i += 1 resp = RESTcall(uri) break if resp.code == "200" or i > 3 end if i > 3 case resp.code ## This is the HTTP response code from the REST API call. when "400" puts "--->[400] Item(s) not currently available?" when "401" puts "--->[401] Not Authorized to Access." when "403" puts "--->[403] Verboten" when "404" puts "--->[404] Item(s) Not Found." when "500" puts "--->[500] Not Definded to API server?" end else api = JSON.parse(resp.body) if api.has_key?('items') ## if container, not end parameter if api['totalItems'].to_i < 50 ## some containers have hundreds!! api['items'].each do |r| if r.has_key?('selfLink') stack.push r['selfLink'] if api['selfLink'] != r['selfLink'].chop ## some items reference themselves?!?! else stack.push r['reference']['link'] ## v11.4 Build 145 has a different format from Beta I. end end else puts "---> Too many Items to store (#{api['totalItems']})" end end end break if stack.empty? end exit197Views0likes1CommentRuby Virtual Server and Pool Creation
Problem this snippet solves: A simple script to add and delete both Virtual IPs and Pools in: Ruby Code : require 'rubygems' require 'rest-client' require 'json' # define program-wide variables BIGIP_ADDRESS = 'test-ltm-03.element.local' BIGIP_USER = 'admin' BIGIP_PASS = 'admin' SLEEP_TIME = 20 VS_NAME = 'test-http-virtual_ruby' VS_ADDRESS = '1.1.1.1' VS_PORT = '80' POOL_NAME = 'test-http-pool_ruby' POOL_LB_METHOD = 'least-connections-member' POOL_MEMBERS = [ '10.0.0.1:80', '10.0.0.2:80', '10.0.0.3:80' ] # create/delete methods def create_pool bigip, name, members, lb_method # convert member format members.collect { |member| { :kind => 'ltm:pool:members', :name => member} } # define test pool payload = { :kind => 'tm:ltm:pool:poolstate', :name => name, :description => 'A Ruby rest-client test pool', :loadBalancingMode => lb_method, :monitor => 'http', :members => members } bigip['ltm/pool'].post payload.to_json end def create_http_virtual bigip, name, address, port, pool # define test virtual payload = { :kind => 'tm:ltm:virtual:virtualstate', :name => name, :description => 'A Ruby rest-client test virtual server', :destination => "#{address}:#{port}", :mask => '255.255.255.255', :ipProtocol => 'tcp', :sourceAddressTranslation => { :type => 'automap' }, :profiles => [ { :kind => 'ltm:virtual:profile', :name => 'http' }, { :kind => 'ltm:virtual:profile', :name => 'tcp' } ], :pool => pool } bigip['ltm/virtual'].post payload.to_json end def delete_pool bigip, name url = "ltm/pool/#{name}" bigip[url].delete end def delete_virtual bigip, name url = "ltm/virtual/#{name}" bigip[url].delete end # REST resource for BIG-IP that all other requests will use bigip = RestClient::Resource.new( "https://#{BIGIP_ADDRESS}/mgmt/tm/", :user => BIGIP_USER, :password => BIGIP_PASS, :headers => { :content_type => 'application/json' } ) puts "created REST resource for BIG-IP at #{BIGIP_ADDRESS}..." # create pool create_pool bigip, POOL_NAME, POOL_MEMBERS, POOL_LB_METHOD puts "created pool \"#{POOL_NAME}\" with members #{POOL_MEMBERS.join(', ')}..." # create virtual create_http_virtual bigip, VS_NAME, VS_ADDRESS, VS_PORT, POOL_NAME puts "created virtual server \"#{VS_NAME}\" with destination #{VS_ADDRESS}:#{VS_PORT}..." # sleep for a little while puts "sleeping for #{SLEEP_TIME} seconds, check for successful creation..." sleep SLEEP_TIME # delete virtual delete_virtual bigip, VS_NAME puts "deleted virtual server \"#{VS_NAME}\"..." # delete pool delete_pool bigip, POOL_NAME puts "deleted pool \"#{POOL_NAME}\"..."222Views0likes1CommentVirtual Address ARP Switcher
Problem this snippet solves: The virtual-address-arp-switcher script is used to quickly and efficiently migrate virtual addresses from one BIG-IP to another. The BIG-IP that is active and currently holds the ARP-enabled virtual address is referred to as the 'source'. The BIG-IP holding the same virtual address with ARP disabled is the 'target'. This script will login to both units, ensure that the virtual address is present on both units and that the ARP setting for both the source and target are in their correct state. A delay can be inserted during which both virtual addresses are in the disabled state. The delay can be set to the ARP cache timeout to ensure that duplicates ARP entries do not exist in the caches. How to use this snippet: Requirements Ruby Ruby Gems F5 iControl for Ruby library (http://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/1086421/Getting-Started-With-Ruby-and-iControl.aspx) other dependencies will be installed with the iControl gem Installation Steps Install Ruby, Ruby's OpenSSL library, Ruby Gems, and the Ruby iControl libraries http://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/1086421/Getting-Started-With-Ruby-and-iControl.aspx Copy this code to /usr/local/bin and chmod +x to make the script executable Run it! virtual-address-arp-switcher --bigip-address-source 10.0.0.10 --bigip-address-target 10.0.0.20 -u admin -p admin -v 192.168.22.44 -d 10 Code : #!/usr/bin/ruby # == Synopsis # # virtual-address-arp-switcher - quickly swap virtual addresses between BIG-IPs # # The virtual-address-arp-switcher script is used to quickly and efficiently migrate virtual addresses from one BIG-IP to another. The BIG-IP that is active and currently holds the ARP-enabled virtual address is referred to as the 'source'. The BIG-IP holding the same virtual address with ARP disabled is the 'target'. This script will login to both units, ensure that the virtual address is present on both units and that the ARP setting for both the source and target are in their correct state. A delay can be inserted during which both virtual addresses are in the disabled state. The delay can be set to the ARP cache timeout to ensure that duplicates ARP entries do not exist in the caches. # # == Usage # # virtual-address-arp-switcher [OPTIONS] # # -h, --help: # show help # # --bigip-address-source, -b [hostname]: # specify the hostname or IP address for source BIG-IP # # --bigip-user-source, -u [username]: # username for source BIG-IP # # --bigip-pass-source, -p [password]: # password for source BIG-IP # # --bigip-address-target [hostname]: # specify the target BIG-IP address # # --bigip-user-target [username]: # username for target BIG-IP, by default assumes same as source BIG-IP # # --bigip-pass-target [password]: # password for target BIG-IP, by default assumes same as source BIG-IP # # --virtual-address, -v [ip address]: # virtual address for which to disable and enable ARP # # --delay, -d [seconds]: # delay between disabling ARP on the source BIG-IP and enabling on target require 'rubygems' require 'f5-icontrol' require 'getoptlong' require 'rdoc/usage' options = GetoptLong.new( [ '--bigip-address-source', '-b', GetoptLong::REQUIRED_ARGUMENT ], [ '--bigip-user-source', '-u', GetoptLong::REQUIRED_ARGUMENT ], [ '--bigip-pass-source', '-p', GetoptLong::REQUIRED_ARGUMENT ], [ '--bigip-address-target', GetoptLong::REQUIRED_ARGUMENT ], [ '--bigip-user-target', GetoptLong::REQUIRED_ARGUMENT ], [ '--bigip-pass-target', GetoptLong::REQUIRED_ARGUMENT ], [ '--virtual-address', '-v', GetoptLong::REQUIRED_ARGUMENT ], [ '--delay', '-d', GetoptLong::REQUIRED_ARGUMENT ], [ '--help', '-h', GetoptLong::NO_ARGUMENT ] ) # set inital values bigip_address_source = '' bigip_user_source = '' bigip_pass_source = '' bigip_address_target = '' bigip_user_target = '' bigip_pass_target = '' virtual_address = '' delay = 0 options.each do |option, arg| case option when '--bigip-address-source' bigip_address_source = arg when '--bigip-user-source' bigip_user_source = arg when '--bigip-pass-source' bigip_pass_source = arg when '--bigip-address-target' bigip_address_target = arg when '--bigip-user-target' bigip_user_target = arg when '--bigip-pass-target' bigip_pass_target = arg when '--virtual-address' if arg =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ virtual_address = arg end when '--delay' delay = arg.to_i when '--help' RDoc::usage end end RDoc::usage if bigip_address_source.empty? \ or bigip_user_source.empty? \ or bigip_pass_source.empty? \ or bigip_address_target.empty? \ or virtual_address.empty? bigip_user_target = bigip_user_source if bigip_user_target.empty? bigip_pass_target = bigip_pass_source if bigip_pass_target.empty? # Initiate SOAP RPC connection to BIG-IP begin bigip_source = F5::IControl.new(bigip_address_source, bigip_user_source, bigip_pass_source, ['LocalLB.VirtualAddress']).get_interfaces rescue puts 'ERROR: Connection error occured while connecting to the source BIG-IP at ' puts "#{bigip_address_source}." exit 1 end begin bigip_target = F5::IControl.new(bigip_address_target, bigip_user_target, bigip_pass_target, ['LocalLB.VirtualAddress']).get_interfaces rescue puts 'ERROR: Connection error occured while connecting to the target BIG-IP at ' puts "#{bigip_address_target}." exit 1 end # Make sure that the virtual address is available on both BIG-IPs unless bigip_source['LocalLB.VirtualAddress'].get_list.include? virtual_address puts 'ERROR: The source BIG-IP does not contain the virtual address provided.' exit 1 end unless bigip_target['LocalLB.VirtualAddress'].get_list.include? virtual_address puts 'ERROR: The target BIG-IP does not contain the virtual address provided.' exit 1 end # Ensure that the ARP state of both addresses is correct bigip_source_arp_state = bigip_source['LocalLB.VirtualAddress'].get_arp_state(virtual_address).to_s bigip_target_arp_state = bigip_target['LocalLB.VirtualAddress'].get_arp_state(virtual_address).to_s if bigip_source_arp_state == 'STATE_DISABLED' and bigip_target_arp_state == 'STATE_ENABLED' puts 'ERROR: ARP is currently disabled for the virtual address on the source BIG-IP and enabled on the target. It appears that the two are reversed. Please read the help message for more information on which unit should be the source and the other the target.' exit 1 elsif (bigip_source_arp_state and bigip_target_arp_state) == 'STATE_ENABLED' puts 'ERROR: ARP is enabled for the virtual address on both BIG-IPs. It is likely that there is an IP address conflict at the moment.' exit 1 elsif bigip_source_arp_state == 'STATE_DISABLED' puts 'ERROR: ARP is currently disabled for the virtual address on the source BIG-IP and needs to be enabled to proceed.' exit 1 elsif bigip_target_arp_state == 'STATE_ENABLED' puts 'ERROR: ARP is currently enabled for the virtual address on the target BIG-IP and needs to be disabled to proceed.' exit 1 end # Everything looks good so far, confirm the swap, then proceed puts "Virtual address details" puts "-" * 20 puts "Virtual address: #{virtual_address}" puts "Current location: #{bigip_address_source}" puts "Future location: #{bigip_address_target}" puts "Delay: #{delay} seconds" answer = '' print "\nAre you sure you want to proceed? (no/yes) " STDOUT.flush answer = STDIN.gets.chomp exit unless answer == "yes" # Begin the swap puts "WARNING: Commencing swap! Do not exit script! Wait for exit!" puts "INFO: Disabling ARP for virtual address #{virtual_address} on #{bigip_address_source}..." bigip_source['LocalLB.VirtualAddress'].set_arp_state([virtual_address], ['STATE_DISABLED']) puts "INFO: Sleeping for #{delay} seconds..." sleep delay puts "INFO: Enabling ARP for virtual address #{virtual_address} on #{bigip_address_target}..." bigip_target['LocalLB.VirtualAddress'].set_arp_state([virtual_address], ['STATE_ENABLED'])200Views0likes0CommentsSSLKeyAndCSRCreator
Problem this snippet solves: This is a script will generate a private key and output a certificate signing request. This is all done from a workstation command line and communicated to the BIG-IP via iControl. How to use this snippet: Requirements Ruby Ruby Gems F5 iControl for Ruby library other dependencies will be installed with the iControl gem Installation Steps Install Ruby, Ruby's ""OpenSSL"" library, Ruby Gems, and the Ruby iControl libraries Getting-Started-With-Ruby-and-iControl.aspx Copy this code to /usr/local/bin and chmod +x to make the script executable Run it! minimal options: ssl-key-and-csr-creator.rb -b 192.168.1.245 -u admin -i test-key-001 full options: ssl-key-and-csr-creator.rb -b 192.168.1.245 -u admin -p admin -i test-key-001 -t RSA -l 4096 -s fips --common-name=www.example.com --country=US --state=Washington --locality=Seattle --organization="Example Company, Inc." --division="Information Technology" -o /my-ssl-directory/ -k -c Code : #!/usr/bin/ruby require "rubygems" require "f5-icontrol" require "getoptlong" options = GetoptLong.new( [ "--bigip-address", "-b", GetoptLong::REQUIRED_ARGUMENT ], [ "--bigip-user", "-u", GetoptLong::REQUIRED_ARGUMENT ], [ "--bigip-pass", "-p", GetoptLong::REQUIRED_ARGUMENT ], [ "--key-id", "-i", GetoptLong::REQUIRED_ARGUMENT ], [ "--key-type", "-t", GetoptLong::OPTIONAL_ARGUMENT ], [ "--key-bit-length", "-l", GetoptLong::OPTIONAL_ARGUMENT ], [ "--key-security", "-s", GetoptLong::OPTIONAL_ARGUMENT ], [ "--common-name", GetoptLong::OPTIONAL_ARGUMENT ], [ "--country", GetoptLong::OPTIONAL_ARGUMENT ], [ "--state", GetoptLong::OPTIONAL_ARGUMENT ], [ "--locality", GetoptLong::OPTIONAL_ARGUMENT ], [ "--organization", GetoptLong::OPTIONAL_ARGUMENT ], [ "--division", GetoptLong::OPTIONAL_ARGUMENT ], [ "--output-dir", "-o", GetoptLong::OPTIONAL_ARGUMENT ], [ "--key-output", "-k", GetoptLong::NO_ARGUMENT ], [ "--csr-output", "-c", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ] ) def usage puts $0 + " -b -u -i " puts puts "BIG-IP connection parameters" puts "-" * 20 puts " -b (--bigip-address) BIG-IP management-accessible address" puts " -u (--bigip-user) BIG-IP username" puts " -p (--bigip-pass) BIG-IP password (will prompt if left blank" puts puts "Private key parameters" puts "-" * 20 puts " -i (--key-id) key ID: must be unique and should be indicative of the purpose (required)" puts " -t (--key-type) key type: [RSA|DSA] (default is 'RSA')" puts " -l (--key-bit-length) key bit length: should be a minimum of 1024-bit (default is 2048; most CAs won't sign weaker keys)" puts " -s (--key-security) key security: [normal|fips|password] (default is 'normal' with no passphrase)" puts puts "X.509 data parameters (if blank, you'll be prompted for the answers)" puts "-" * 20 puts " (--common-name) common name: FQDN for virtual server (www.example.com)" puts " (--country) country: two letter country abbreviation (US, CN, etc.)" puts " (--state) state: two letter state abbreviation (WA, OR, CA, etc.)" puts " (--locality) locality: locality or city name (Seattle, Portland, etc.)" puts " (--organization) organization: organization or company name (F5 Networks, Company XYZ, etc.)" puts " (--division) division: department or division name (IT, HR, Finance, etc.)" puts puts "Output options" puts "-" * 20 puts " -o (--output-dir) CSR/key output directory: location to output private key and CSR files (defaults to current working directory)" puts " -k (--key-output) key output: save private key to a local file (saved as key_id.key)" puts " -c (--csr-output) CSR output: save certificate signing request to a local file (saved as key_id.csr)" puts puts "Help and usage" puts "-" * 20 puts " -h (--help) shows this help/usage dialog" puts exit end # set STDOUT buffer to synchronous STDOUT.sync = true # global variables KEY_TYPES = { "RSA" => "KTYPE_RSA_PRIVATE", "DSA" => "KTYPE_DSA_PRIVATE" } KEY_SECURITIES = { "normal" => "STYPE_NORMAL", "fips" => "STYPE_FIPS", "password" => "STYPE_PASSWORD" } # initial parameter values overwrite_key = false # key/CSR default output file values key_output = false csr_output = false output_dir = Dir.pwd # BIG-IP connection parameters bigip = {} bigip['address'] = '' bigip['user'] = '' bigip['pass'] = '' # private key parameters key_data = {} key_data['id'] = '' key_data['key_type'] = KEY_TYPES["RSA"] key_data['bit_length'] = 2048 key_data['security'] = KEY_SECURITIES["normal"] # X.509 data parameters x509_data = {} x509_data['common_name'] = '' x509_data['country_name'] = '' x509_data['state_name'] = '' x509_data['locality_name'] = '' x509_data['organization_name'] = '' x509_data['division_name'] = '' # loop through command line options options.each do |option, arg| case option when "--bigip-address" bigip['address'] = arg when "--bigip-user" bigip['user'] = arg when "--bigip-pass" bigip['pass'] = arg when "--key-id" key_data['id'] = arg when "--key-type" if KEY_TYPES.keys.include? arg.upcase key_data['key_type'] = KEY_TYPES[arg.upcase] else puts "Error: Invalid key type. Exiting." exit 1 end when "--key-bit-length" key_data['bit_length'] = arg.to_i when "--key-security" if KEY_SECURITIES.keys.include? arg.downcase key_data['security'] = KEY_SECURITIES[arg.downcase] else puts "Error: Invalid key security type. Exiting." exit 1 end when "--common-name" x509_data['common_name'] = arg when "--country" if arg =~ /[a-z]{2}/i x509_data['country_name'] = arg.upcase else puts "Error: Use exactly two letters for the country code. Exiting." exit 1 end when "--state" x509_data['state_name'] = arg when "--locality" x509_data['locality_name'] = arg when "--organization" x509_data['organization_name'] = arg when "--division" x509_data['division_name'] = arg when "--output-dir" if File.directory? arg output_dir = arg else puts "Error: Invalid directory for output. Exiting." end when "--key-output" key_output = true when "--csr-output" csr_output = true when "--help" usage end end # we need at least the BIG-IP's address, user, and a key ID to proceed usage if bigip['address'].empty? or bigip['user'].empty? or key_data['id'].empty? if bigip['pass'].empty? puts "Please enter the BIG-IPs password..." print "Password: " system("stty", "-echo") bigip['pass'] = gets.chomp system("stty", "echo") puts end # set up connection to BIG-IP and Management.KeyCertificate interface bigip = F5::IControl.new(bigip['address'], bigip['user'], bigip['pass'], ["Management.KeyCertificate"]).get_interfaces #grab a list of existing keys and confirm overwrite if a conflict exists existing_keys = bigip["Management.KeyCertificate"].get_key_list('MANAGEMENT_MODE_DEFAULT').collect { |key| key["key_info"]["id"] } if existing_keys.include? key_data['id'] print "A key with an ID of '#{key_data['id']}' already exists. Overwrite it? (yes/no) " answer = gets.chomp if answer !~ /^yes$/i puts "Will not overwrite existing key. Exiting." exit else overwrite_key = true end end # time to play 20 questions with the X.509 data if x509_data.values.delete_if { |value| !value.empty? }.size > 0 puts "Please fill in the following X.509 data parameters..." end x509_data.sort.each do |key, value| if value.empty? print key.capitalize.gsub('_', ' ') + "? " x509_data[key] = gets.chomp end end bigip["Management.KeyCertificate"].key_generate('MANAGEMENT_MODE_DEFAULT', [key_data], [x509_data], true, overwrite_key) # write private key to local file if specified by user if key_output key_output_file = output_dir + "/" + key_data['id'] + ".key" key = bigip["Management.KeyCertificate"].key_export_to_pem('MANAGEMENT_MODE_DEFAULT', [key_data['id']])[0] File.open(key_output_file, 'w') { |file| file.write(key) } end # display subject information for CSR as well as the CSR puts "Certificate Request" puts "-" * 20 puts "Subject: C=#{x509_data['country_name']}, ST=#{x509_data['state_name']}, L=#{x509_data['locality_name']}, O=#{x509_data['organization_name']}, OU=#{x509_data['division_name']}, CN=#{x509_data['common_name']}" csr = bigip["Management.KeyCertificate"].certificate_request_export_to_pem('MANAGEMENT_MODE_DEFAULT', [key_data['id']]) # write csr key to local file if specified by user if csr_output csr_output_file = output_dir + "/" + key_data['id'] + ".csr" File.open(csr_output_file, 'w') { |file| file.write(csr) } end puts puts csr250Views0likes0Comments