Module: Shells::PfSenseCommon
- Included in:
- PfSenseSerialSession, PfSenseSshSession
- Defined in:
- lib/shells/pf_sense_common.rb
Overview
Common functionality for interacting with a pfSense device.
Defined Under Namespace
Classes: MenuNavigationFailure, PublicKeyInvalid, PublicKeyNotFound, RestartNow, UserNotFound
Constant Summary collapse
- BASE_SHELL =
The base shell used when possible.
'/bin/sh'- PF_SHELL =
The pfSense shell itself.
'/usr/local/sbin/pfSsh.php'- PF_PROMPT =
The prompt in the pfSense shell.
'pfSense shell:'
Instance Attribute Summary collapse
-
#pf_sense_host ⇒ Object
Gets the hostname of the pfSense device.
-
#pf_sense_user ⇒ Object
Gets the user currently logged into the pfSense device.
-
#pf_sense_version ⇒ Object
Gets the version of the pfSense firmware.
Class Method Summary collapse
-
.included(base) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#apply_filter_config ⇒ Object
Apply the firewall configuration.
-
#apply_user_config(user_id) ⇒ Object
Applies the user configuration for the specified user.
-
#config_parsed? ⇒ Boolean
Determines if the configuration has been parsed during this session.
-
#enable_cert_auth(public_key = '~/.ssh/id_rsa.pub') ⇒ Object
Enabled public key authentication for the current pfSense user.
-
#exec_prompt(&block) ⇒ Object
:nodoc:.
-
#exec_shell(&block) ⇒ Object
:nodoc:.
-
#get_config_section(section_name) ⇒ Object
Gets a configuration section from the pfSense device.
-
#line_ending ⇒ Object
:nodoc:.
-
#parse_config ⇒ Object
Reloads the pfSense configuration on the device.
-
#pf_exec(*commands) ⇒ Object
Executes a series of commands on the pfSense shell.
-
#quit ⇒ Object
Exits the shell session immediately.
-
#reboot ⇒ Object
Exits the shell session immediately and requests a reboot of the pfSense device.
-
#set_config_section(section_name, values, message = '') ⇒ Object
Sets a configuration section to the pfSense device.
-
#validate_options ⇒ Object
:nodoc:.
Instance Attribute Details
#pf_sense_host ⇒ Object
Gets the hostname of the pfSense device.
63 64 65 |
# File 'lib/shells/pf_sense_common.rb', line 63 def pf_sense_host @pf_sense_host end |
#pf_sense_user ⇒ Object
Gets the user currently logged into the pfSense device.
59 60 61 |
# File 'lib/shells/pf_sense_common.rb', line 59 def pf_sense_user @pf_sense_user end |
#pf_sense_version ⇒ Object
Gets the version of the pfSense firmware.
55 56 57 |
# File 'lib/shells/pf_sense_common.rb', line 55 def pf_sense_version @pf_sense_version end |
Class Method Details
.included(base) ⇒ Object
:nodoc:
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/shells/pf_sense_common.rb', line 70 def self.included(base) #:nodoc: # Trap the RestartNow exception. # When encountered, change the :quit option to '/sbin/reboot'. # This requires rewriting the @options instance variable since the hash is frozen # after initial validation. base.on_exception do |shell, ex| if ex.is_a?(Shells::PfSenseCommon::RestartNow) shell.send(:change_quit, '/sbin/reboot') true else false end end end |
Instance Method Details
#apply_filter_config ⇒ Object
Apply the firewall configuration.
You need to apply the firewall configuration after you make changes to aliases, NAT rules, or filter rules.
201 202 203 204 205 206 207 |
# File 'lib/shells/pf_sense_common.rb', line 201 def apply_filter_config pf_exec( 'require_once("shaper.inc");', 'require_once("filter.inc");', 'filter_configure_sync();' ) end |
#apply_user_config(user_id) ⇒ Object
Applies the user configuration for the specified user.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/shells/pf_sense_common.rb', line 211 def apply_user_config(user_id) user_id = user_id.to_i pf_exec( 'require_once("auth.inc");', "$user_entry = $config[\"system\"][\"user\"][#{user_id}];", '$user_groups = array();', 'foreach ($config["system"]["group"] as $gidx => $group) {', ' if (is_array($group["member"])) {', " if (in_array(#{user_id}, $group[\"member\"])) { $user_groups[] = $group[\"name\"]; }", ' }', '}', # Intentionally run set_groups before and after to ensure group membership gets fully applied. 'local_user_set_groups($user_entry, $user_groups);', 'local_user_set($user_entry);', 'local_user_set_groups($user_entry, $user_groups);' ) end |
#config_parsed? ⇒ Boolean
Determines if the configuration has been parsed during this session.
165 166 167 |
# File 'lib/shells/pf_sense_common.rb', line 165 def config_parsed? instance_variable_defined?(:@config_parsed) && instance_variable_get(:@config_parsed) end |
#enable_cert_auth(public_key = '~/.ssh/id_rsa.pub') ⇒ Object
Enabled public key authentication for the current pfSense user.
Once this has been done you should be able to connect without using a password.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/shells/pf_sense_common.rb', line 233 def enable_cert_auth(public_key = '~/.ssh/id_rsa.pub') cert_regex = /^ssh-[rd]sa (?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)? \S*$/m # get our cert unless the user provided a full cert for us. unless public_key =~ cert_regex public_key = File.(public_key) if File.exist?(public_key) public_key = File.read(public_key).to_s.strip else raise Shells::PfSenseCommon::PublicKeyNotFound end raise Shells::PfSenseCommon::PublicKeyInvalid unless public_key =~ cert_regex end cfg = get_config_section 'system' user_id = nil user_name = [:user].downcase cfg['user'].each_with_index do |user,index| if user['name'].downcase == user_name user_id = index authkeys = Base64.decode64(user['authorizedkeys'].to_s).gsub("\r\n", "\n").strip unless authkeys == '' || authkeys =~ cert_regex warn "Existing authorized keys for user #{options[:user]} are invalid and are being reset." authkeys = '' end if authkeys == '' user['authorizedkeys'] = Base64.strict_encode64(public_key) else authkeys = authkeys.split("\n") unless authkeys.include?(public_key) authkeys << public_key unless authkeys.include?(public_key) user['authorizedkeys'] = Base64.strict_encode64(authkeys.join("\n")) end end break end end raise Shells::PfSenseCommon::UserNotFound unless user_id set_config_section 'system', cfg, "Enable certificate authentication for #{options[:user]}." apply_user_config user_id end |
#exec_prompt(&block) ⇒ Object
:nodoc:
137 138 139 140 141 142 143 144 145 146 |
# File 'lib/shells/pf_sense_common.rb', line 137 def exec_prompt(&block) #:nodoc: debug 'Initializing pfSense shell...' exec '/usr/local/sbin/pfSsh.php', command_timeout: 5 begin block.call ensure debug 'Quitting pfSense shell...' send_data 'exit' + line_ending end end |
#exec_shell(&block) ⇒ Object
:nodoc:
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 |
# File 'lib/shells/pf_sense_common.rb', line 98 def exec_shell(&block) #:nodoc: super do # We want to drop to the shell before executing the block. # So we'll navigate the menu to get the option for the shell. # For this first navigation we allow a delay only if we are not connected to a serial device. # Serial connections are always on, so they don't need to initialize first. = 'Shell', !(Shells::SerialSession > self.class) raise unless # For 2.3 and 2.4 this is a valid match. # If future versions change the default prompt, we need to change our process. # [VERSION][USER@HOSTNAME]/root: where /root is the current dir. shell_regex = /\[(?<VER>[^\]]*)\]\[(?<USERHOST>[^\]]*)\](?<CD>\/.*):\s*$/ # Now we execute the menu option and wait for the shell_regex to match. temporary_prompt(shell_regex) do exec .to_s, command_timeout: 5 # Once we have a match we should be able to repeat it and store the information from the shell. data = prompt_match.match(combined_output) self.pf_sense_version = data['VER'] self.pf_sense_user, _, self.pf_sense_host = data['USERHOST'].partition('@') end block.call # Wait for the shell_regex to match again. temporary_prompt(shell_regex) { wait_for_prompt nil, 4, false } # Exit the shell to return to the menu. send_data 'exit' + line_ending # After the block we want to know what the Logout option is and we change the quit command to match. = 'Logout' raise unless change_quit .to_s end end |
#get_config_section(section_name) ⇒ Object
Gets a configuration section from the pfSense device.
171 172 173 174 |
# File 'lib/shells/pf_sense_common.rb', line 171 def get_config_section(section_name) parse_config unless config_parsed? JSON.parse pf_exec("echo json_encode($config[#{section_name.to_s.inspect}]);").strip end |
#line_ending ⇒ Object
:nodoc:
66 67 68 |
# File 'lib/shells/pf_sense_common.rb', line 66 def line_ending #:nodoc: "\n" end |
#parse_config ⇒ Object
Reloads the pfSense configuration on the device.
158 159 160 161 |
# File 'lib/shells/pf_sense_common.rb', line 158 def parse_config pf_exec 'parse_config(true);' @config_parsed = true end |
#pf_exec(*commands) ⇒ Object
Executes a series of commands on the pfSense shell.
150 151 152 153 154 |
# File 'lib/shells/pf_sense_common.rb', line 150 def pf_exec(*commands) ret = '' commands.each { |cmd| ret += exec(cmd) } ret + exec('exec') end |
#quit ⇒ Object
Exits the shell session immediately.
292 293 294 295 |
# File 'lib/shells/pf_sense_common.rb', line 292 def quit raise Shells::SessionCompleted if session_complete? raise Shells::ShellBase::QuitNow end |
#reboot ⇒ Object
Exits the shell session immediately and requests a reboot of the pfSense device.
285 286 287 288 |
# File 'lib/shells/pf_sense_common.rb', line 285 def reboot raise Shells::SessionCompleted if session_complete? raise Shells::PfSenseCommon::RestartNow end |
#set_config_section(section_name, values, message = '') ⇒ Object
Sets a configuration section to the pfSense device.
Returns the number of changes made to the configuration.
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/shells/pf_sense_common.rb', line 180 def set_config_section(section_name, values, = '') current_values = get_config_section(section_name) changes = generate_config_changes("$config[#{section_name.to_s.inspect}]", current_values, values) if changes&.any? if .to_s.strip == '' = "Updating #{section_name} section." end changes << "write_config(#{message.inspect});" pf_exec(*changes) (changes.size - 1) else 0 end end |
#validate_options ⇒ Object
:nodoc:
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/shells/pf_sense_common.rb', line 87 def #:nodoc: super [:shell] = :shell [:prompt] = 'pfSense shell:' [:quit] = 'exit' [:retrieve_exit_code] = false [:on_non_zero_exit_code] = :ignore [:override_set_prompt] = ->(sh) { true } [:override_get_exit_code] = ->(sh) { 0 } end |