[server] Added FORCE_SNAT to access.conf stanzas.

Added FORCE_SNAT to the access.conf file so that per-access stanza SNAT
criteria can be specified for SPA access.
This commit is contained in:
Michael Rash 2013-12-04 21:52:07 -05:00
parent d7aa820e33
commit e0114e60c2
7 changed files with 144 additions and 30 deletions

View File

@ -29,6 +29,8 @@ fwknop-2.5.2 (//2013):
- [server] Bug fix for SPA NAT modes on iptables firewalls to ensure that
custom fwknop chains are re-created if they get deleted out from under
the running fwknopd instance.
- [server] Added FORCE_SNAT to the access.conf file so that per-access
stanza SNAT criteria can be specified for SPA access.
- [test suite] added --gdb-test to allow a previously executed fwknop
or fwknopd command to be sent through gdb with the same command line
args as the test suite used. This is for convenience to rapidly allow

View File

@ -443,6 +443,16 @@ directive starts a new stanza.
for each stanza in the access.conf file. This way, multiple external
users can each directly access only one internal system per SPA key.
*FORCE_SNAT* '<IP>'::
For any valid SPA packet, add an SNAT rule in addition to any DNAT rule
created with a corresponding (required) FORCE_NAT variable. This is
analogous to SNAT_TRANSLATE_IP from the '@sysconfdir@/fwknop/fwknopd.conf'' file
except that it is per access stanza and overrides any value set with
SNAT_TRANSLATE_IP. This is useful for situations where an incoming NAT'd
connection may be otherwise unanswerable due to routing constraints (i.e.
the system receiving the SPA authenticated connection has a default route
to a different device than the SPA system itself).
*GPG_HOME_DIR* '<path>'::
Define the path to the GnuPG directory to be used by the *fwknopd*
server. If this keyword is not specified within '@sysconfdir@/fwknop/access.conf' then

View File

@ -165,7 +165,6 @@ add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *v
if (sscanf(val, "%15s %5u", ip_str, &curr_acc->force_nat_port) != 2)
{
log_msg(LOG_ERR,
"[*] Fatal: invalid FORCE_NAT arg '%s', need <IP> <PORT>",
val
@ -192,6 +191,32 @@ add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *v
return;
}
static void
add_acc_force_snat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *val)
{
char ip_str[MAX_IPV4_STR_LEN] = {0};
if (sscanf(val, "%15s", ip_str) != 1)
{
log_msg(LOG_ERR,
"[*] Fatal: invalid FORCE_SNAT arg '%s', need <IP>", val);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
if(! is_valid_ipv4_addr(ip_str))
{
log_msg(LOG_ERR,
"[*] Fatal: invalid FORCE_NAT IP '%s'", ip_str);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
curr_acc->force_snat = 1;
add_acc_string(&(curr_acc->force_snat_ip), ip_str);
return;
}
#endif
/* Take an IP or Subnet/Mask and convert it to mask for later
@ -680,6 +705,9 @@ free_acc_stanza_data(acc_stanza_t *acc)
if(acc->force_nat_ip != NULL)
free(acc->force_nat_ip);
if(acc->force_nat_ip != NULL)
free(acc->force_snat_ip);
if(acc->key != NULL)
{
zero_buf_wrapper(acc->key, acc->key_len);
@ -955,6 +983,15 @@ acc_data_is_valid(acc_stanza_t * const acc)
}
}
if(acc->force_snat == 1 && acc->force_nat == 0)
{
log_msg(LOG_ERR,
"[*] FORCE_SNAT implies FORCE_NAT must also be used for access stanza source: '%s'",
acc->source
);
return(0);
}
if(acc->require_source_address == 0)
{
log_msg(LOG_INFO,
@ -1338,6 +1375,24 @@ parse_access_file(fko_srv_options_t *opts)
"[*] FORCE_NAT not supported.");
fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
#endif
}
else if(CONF_VAR_IS(var, "FORCE_SNAT"))
{
#if FIREWALL_IPTABLES
if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0 )
{
log_msg(LOG_ERR,
"[*] FORCE_SNAT_NAT requires ENABLE_IPT_FORWARDING to be enabled in fwknopd.conf");
fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
add_acc_force_snat(opts, curr_acc, val);
#else
log_msg(LOG_ERR,
"[*] FORCE_SNAT not supported.");
fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
#endif
}
else

View File

@ -961,7 +961,7 @@ process_spa_request(const fko_srv_options_t * const opts,
struct fw_chain * const dnat_chain = &(opts->fw_config->chain[IPT_DNAT_ACCESS]);
struct fw_chain *snat_chain; /* We assign this later (if we need to). */
int res = 0, is_err;
int res = 0, is_err, snat_chain_num = 0;
time_t now;
unsigned int exp_ts;
@ -1260,29 +1260,27 @@ process_spa_request(const fko_srv_options_t * const opts,
/* If SNAT (or MASQUERADE) is wanted, then we add those rules here as well.
*/
if(strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0)
if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0)
{
/* Setup some parameter depending on whether we are using SNAT
* or MASQUERADE.
/* Add SNAT or MASQUERADE rules.
*/
if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
&& strncasecmp(opts->config[CONF_SNAT_TRANSLATE_IP], "__CHANGEME__", 10)!=0)
if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip))
{
/* Using static SNAT */
snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
"--to-source %s:%i", acc->force_snat_ip, fst_port);
snat_chain_num = IPT_SNAT_ACCESS;
}
else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
&& is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
{
/* Using static SNAT */
snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
"--to-source %s:%i", opts->config[CONF_SNAT_TRANSLATE_IP],
fst_port);
/* Check to make sure that the jump rules exist for each
* required chain
*/
if(chain_exists(opts, IPT_SNAT_ACCESS) == 0)
create_chain(opts, IPT_SNAT_ACCESS);
if(jump_rule_exists(opts, IPT_SNAT_ACCESS) == 0)
add_jump_rule(opts, IPT_SNAT_ACCESS);
snat_chain_num = IPT_SNAT_ACCESS;
}
else
{
@ -1290,17 +1288,15 @@ process_spa_request(const fko_srv_options_t * const opts,
snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
"--to-ports %i", fst_port);
/* Check to make sure that the jump rules exist for each
* required chain
*/
if(chain_exists(opts, IPT_MASQUERADE_ACCESS) == 0)
create_chain(opts, IPT_MASQUERADE_ACCESS);
if(jump_rule_exists(opts, IPT_MASQUERADE_ACCESS) == 0)
add_jump_rule(opts, IPT_MASQUERADE_ACCESS);
snat_chain_num = IPT_MASQUERADE_ACCESS;
}
if(chain_exists(opts, snat_chain_num) == 0)
create_chain(opts, snat_chain_num);
if(jump_rule_exists(opts, snat_chain_num) == 0)
add_jump_rule(opts, snat_chain_num);
memset(rule_buf, 0, CMD_BUFSIZE);
snprintf(rule_buf, CMD_BUFSIZE-1, IPT_SNAT_RULE_ARGS,

View File

@ -321,10 +321,19 @@ typedef struct acc_stanza
time_t access_expire_time;
int expired;
int encryption_mode;
/* DNAT parameters
*/
unsigned char force_nat;
char *force_nat_ip;
char *force_nat_proto;
unsigned int force_nat_port;
/* SNAT parameters
*/
unsigned char force_snat;
char *force_snat_ip;
struct acc_stanza *next;
} acc_stanza_t;

View File

@ -79,6 +79,7 @@ our %cf = (
'invalid_ipt_input_chain6' => "$conf_dir/invalid_ipt_input_chain_6_fwknopd.conf",
'force_nat_access' => "$conf_dir/force_nat_access.conf",
'hmac_force_nat_access' => "$conf_dir/hmac_force_nat_access.conf",
'hmac_force_snat_access' => "$conf_dir/hmac_force_snat_access.conf",
'cmd_access' => "$conf_dir/cmd_access.conf",
'local_nat' => "$conf_dir/local_nat_fwknopd.conf",
'no_flush_init' => "$conf_dir/no_flush_init_fwknopd.conf",
@ -186,6 +187,8 @@ our $fake_ip = '127.0.0.2';
our $spoof_ip = '1.2.3.4';
our $internal_nat_host = '192.168.1.2';
our $force_nat_host = '192.168.1.123';
our $force_nat_host2 = '123.4.4.4';
our $force_snat_host = '33.3.3.3';
our $default_spa_port = 62201;
our $non_std_spa_port = 12345;

View File

@ -846,13 +846,52 @@
$cf{'rc_hmac_b64_key'},
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'nat'} -a $cf{'hmac_force_nat_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/\sto\:$force_nat_host\:22/i],
'server_negative_output_matches' => [qr/\sto\:$internal_nat_host\:22/i],
'server_positive_output_matches' => [qr/\*\/\sto\:$force_nat_host\:22/i],
'server_negative_output_matches' => [qr/\*\/\sto\:$internal_nat_host\:22/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $cf{'nat'},
'key_file' => $cf{'rc_hmac_b64_key'},
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "force SNAT $force_snat_host (tcp/22)",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'cmdline' => "$default_client_args_no_get_key --rc-file " .
$cf{'rc_hmac_b64_key'},
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'snat'} -a $cf{'hmac_force_snat_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/DNAT\s.*\*\/\sto\:$force_nat_host2\:22/i,
qr/SNAT\s.*\*\/\sto\:$force_snat_host\:22/],
'server_negative_output_matches' => [qr/\*\/\sto\:$internal_nat_host\:22/i,
qr/\*\/\sto\:$force_nat_host\:22/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $cf{'snat'},
'key_file' => $cf{'rc_hmac_b64_key'},
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "force SNAT $force_snat_host (ipt flush)",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'cmdline' => "$default_client_args_no_get_key --rc-file " .
$cf{'rc_hmac_b64_key'},
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'snat'} -a $cf{'hmac_force_snat_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/DNAT\s.*\*\/\sto\:$force_nat_host2\:22/i,
qr/SNAT\s.*\*\/\sto\:$force_snat_host\:22/],
'server_negative_output_matches' => [qr/\*\/\sto\:$internal_nat_host\:22/i,
qr/\*\/\sto\:$force_nat_host\:22/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $cf{'snat'},
'key_file' => $cf{'rc_hmac_b64_key'},
'iptables_rm_chains_after_server_start' => $YES,
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
@ -863,8 +902,8 @@
$cf{'rc_hmac_b64_key'},
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'nat'} -a $cf{'hmac_force_nat_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/\sto\:$force_nat_host\:22/i],
'server_negative_output_matches' => [qr/\sto\:$internal_nat_host\:22/i],
'server_positive_output_matches' => [qr/\*\/\sto\:$force_nat_host\:22/i],
'server_negative_output_matches' => [qr/\*\/\sto\:$internal_nat_host\:22/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $cf{'nat'},