-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from jaredhendrickson13/development
v1.0.0
- Loading branch information
Showing
81 changed files
with
27,215 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/.idea/ | ||
*.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,90 @@ | ||
# pfsense-saml2-auth | ||
A SAML2 authentication extension for the pfSense UI | ||
# pfSense SAML2 Authentication | ||
`pfsense-saml2-auth` is a packaged SAML2 authentication extension for the pfSense webConfigurator. Currently, pfSense | ||
only supports local, LDAP and RADIUS authentication and does not support any native multi-factor authentication (MFA). | ||
At this time, there is unfortunately no roadmap for native SAML2 authentication or native MFA options on pfSense. With | ||
[pfSense Plus](https://www.netgate.com/blog/pfsense-plus-21-02-release-and-pfsense-ce-2-5-0-release-now-available.html) | ||
being officially announced, it is unlikely we will see enterprise level features like SAML2 implemented in pfSense CE | ||
anytime soon. This can create major headaches when dealing with security compliance standards such as PCI DSS that may | ||
require MFA on firewall admin logins. `pfsense-saml2-auth` helps alleviate this problem by allowing you to integrate | ||
single sign-on (SSO) with an identity provider such as Okta or OneLogin. In doing so, you will be able use the identity | ||
provider's built-in MFA for pfSense logins and greatly simplify user onboarding.<br><br> | ||
|
||
![sso_login_example_img](docs/img/sso_login.png) | ||
<sub>The 'Login with SSO' option will only appear on the login screen after the package is installed and configured. SAML2 | ||
must be enabled in System > SAML2 for this option to appear.</sub><br> | ||
|
||
|
||
## Key Features | ||
- Easily integrates SSO logins for pfSense without losing any existing authentication functionality. | ||
- Automatically maps groups returned within the SAML2 assertion to groups within pfSense to inherit existing privileges. | ||
No need to create locate users before authenticating. | ||
- Retains pfSense's built-in authentication and change logs. | ||
- Adds the System > SAML2 settings page within the webConfigurator to make setup a breeze. | ||
|
||
## Installation | ||
To install, simply run the following command from the pfSense command line:<br> | ||
`pkg add https://github.com/jaredhendrickson13/pfsense-saml2-auth/releases/latest/download/pfSense-2.5-pkg-saml2-auth.txz` | ||
|
||
To uninstall:<br> | ||
`pkg delete pfSense-pkg-saml2-auth` | ||
|
||
_Note: when pfSense updates this package will be uninstalled. After updating pfSense, the package will need to be | ||
reinstalled to match the updated version_ | ||
|
||
## Supported Versions | ||
Currently, the package fully supports the following pfSense versions including patched versions of the same release: | ||
- pfSense 2.5.0-RELEASE | ||
- pfSense 2.4.5-RELEASE | ||
- pfSense 2.4.4-RELEASE | ||
|
||
Any version not listed is technically unsupported, but may still function. Proceed with caution. | ||
|
||
## Setup | ||
After installation, navigate to System > SAML2 to configure SAML authentication. You will need to obtain a few | ||
items from your IdP to add on this page and you will also need to provide a few items to your IdP from this page. | ||
<br> | ||
|
||
![sso_settings_example_img](docs/img/sso_settings.png) | ||
|
||
_Note: users must hold the `page-all` and/or `page-system-saml2-auth` privilege to access the System > SAML2 page._ | ||
|
||
### Privilege Mapping | ||
There are two ways to map pfSense privileges to SAML2 users. Choose the method that bests suits your identity provider's | ||
capabilities and your specific needs: | ||
|
||
1) Create pfSense groups to match those that exist within your identity provider. For example, | ||
if you have a group within your identity provider named `Network Admins` that you would like to grant pfSense access to, | ||
you would need to create a group within pfSense named `Network Admins` exactly as it appears in your IdP. Ensure this | ||
group's `Scope` value is set to `Remote` within pfSense. Then assign the desired pfSense privileges to the group. Please | ||
note you must configure your IdP to return a group attribute within the SAML assertion that contains a list of groups | ||
the authenticating user belongs to. You can specify the name of the group mapping attribute in System > SAML2 > Identity | ||
Provider Groups Attribute. If your IdP does not return group attributes in the SAML assertion, this method cannot be | ||
used. | ||
![sso_group_mapping_example_img](docs/img/sso_group_mapping.png)<br><br> | ||
|
||
2) Create a local user that matches the authenticating user's username as it appears in your | ||
IdP. You may use a random password for this user to prevent local authentication if needed. After the local user is | ||
created, assign any permissions you would like the user to obtain upon login. Once the user has been created and any | ||
privileges have been assigned, the user will automatically inherit the assigned privileges upon SAML2 logins. Note, | ||
pfSense does not allow emails as local usernames. In the case that your IdP uses email addresses as usernames by | ||
default, you may check the checkbox at System > SAML2 > Filter Email Usernames to only use the username before the @ | ||
symbol. | ||
![sso_user_mapping_example_img](docs/img/sso_user_mapping.png)<br><br> | ||
|
||
## Limitations | ||
- This package is only intended to add SAML2 authentication to the webConfigurator. SAML2 authentication is not made | ||
available for other pfSense services such as SSH, captive portal, OpenVPN, etc. | ||
|
||
## Disclaimers | ||
- This project is in no way affiliated with the pfSense project or it's parent organization Netgate. Any use of the | ||
pfSense name is intended to relate the project to it's developed platform and in no way capitalizes on the | ||
pfSense trademark. By using this software, you acknowledge that no entity can provide support or guarantee | ||
functionality. | ||
- This project was written and tested using Okta as the IdP. While it should support any IdP that supports SAML2 | ||
applications, it cannot be guaranteed to work with your specific IdP. | ||
- While extra precautions are taken to keep this package secure, you should always test thoroughly before implementing | ||
in a production environment. Use this software is at your own risk! | ||
- This package was designed to be a simple, secure, and stable SAML2 implementation for pfSense. Because of this, | ||
additional features are unlikely to be added unless there are changes to the SAML standard itself. The package will | ||
continue to receive updates to address security issues and general bugs, as well as changes to accommodate future | ||
versions of pfSense. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions
22
pfSense-pkg-saml2-auth/files/etc/inc/priv/saml2_auth.priv.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
// Copyright 2021 Jared Hendrickson | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
global $priv_list; | ||
|
||
$priv_list['page-system-saml2-auth'] = array(); | ||
$priv_list['page-system-saml2-auth']['name'] = "WebCfg - System: SAML2 authentication package"; | ||
$priv_list['page-system-saml2-auth']['descr'] = "Allow access to SAML2 authentication package UI"; | ||
$priv_list['page-system-saml2-auth']['match'] = array(); | ||
$priv_list['page-system-saml2-auth']['match'][] = "saml2_auth*"; |
163 changes: 163 additions & 0 deletions
163
pfSense-pkg-saml2-auth/files/etc/inc/saml2_auth/SAML2Auth.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
<?php | ||
// Copyright 2021 Jared Hendrickson | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
require_once("saml2_auth/lib/php-saml-3.5.1/_toolkit_loader.php"); | ||
require_once("config.inc"); | ||
|
||
class SAML2Auth { | ||
public $auth; | ||
private $config; | ||
|
||
# Constructs the saml2_auth object by loading SAML2 settings and creating the OneLogin_Saml2_Auth object | ||
public function __construct() { | ||
session_start(); | ||
global $config; | ||
$this->config = $config; | ||
|
||
# Try to start SAML2 authentication, handle errors accordingly | ||
try { | ||
$this->auth = new OneLogin\Saml2\Auth($this::get_saml_settings()); | ||
} catch (OneLogin\Saml2\Error $error) { | ||
echo $error->getMessage().PHP_EOL; | ||
exit(); | ||
} | ||
} | ||
|
||
# Initiates the SSO login. Requires a URL to redirect to | ||
public function sso($redirect) { | ||
# Mark the SAML2 login as in process and start login | ||
$_SESSION["saml2_started"] = true; | ||
$this->auth->login($redirect); | ||
} | ||
|
||
public function acs() { | ||
# Check the state of SAML2 authentication. Only proceeds if in expected state. | ||
$this->__check_saml2_state(); | ||
|
||
# Remove the saml2_started handler and process our request response | ||
unset($_SESSION['saml2_started']); | ||
$this->auth->processResponse($_SESSION['AuthNRequestID']); | ||
|
||
# If the sign on attempt is valid, map attributes to our session array. | ||
if ($this->auth->isAuthenticated()) { | ||
# Set session data | ||
$_SESSION["saml2_auth"] = true; | ||
$_SESSION['saml2_user_data'] = $this->auth->getAttributes(); | ||
$_SESSION['saml2_name_id'] = $this->auth->getNameId(); | ||
unset($_SESSION['AuthNRequestID']); | ||
|
||
# Support RelayState settings | ||
if (isset($_POST['RelayState']) && OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) { | ||
$this->auth->redirectTo($_POST['RelayState']); | ||
} | ||
} | ||
|
||
# Handle SAML errors | ||
$this->get_saml2_errors(); | ||
} | ||
|
||
public function metadata() { | ||
try { | ||
# Validate the SP metadata and print the XML metadata if valid | ||
$settings = new OneLogin\Saml2\Settings($this->get_saml_settings(), true); | ||
$metadata = $settings->getSPMetadata(); | ||
$errors = $settings->validateMetadata($metadata); | ||
if (empty($errors)) { | ||
header('Content-Type: text/xml'); | ||
echo $metadata; | ||
} else { | ||
throw new OneLogin\Saml2\Error ( | ||
'Invalid SP metadata: '.implode(', ', $errors), | ||
OneLogin\Saml2\Error::METADATA_SP_INVALID | ||
); | ||
} | ||
} catch (Exception $e) { | ||
echo $e->getMessage(); | ||
} | ||
} | ||
|
||
private function __check_saml2_state() { | ||
# Redirect to home page if user is already logged in | ||
if ($_SESSION['Logged_In']) { | ||
header("Location: /saml2_auth/sso/redirect/"); | ||
exit(); | ||
} | ||
# Redirect to SSO login if the login process has not been started | ||
elseif (!$_SESSION['saml2_started']) { | ||
header("Location: /saml2_auth/sso/"); | ||
exit(); | ||
} | ||
} | ||
|
||
public function get_saml2_errors() { | ||
# Print SAML errors if they exist | ||
if (!empty($this->auth->getErrors())) { | ||
session_destroy(); | ||
echo '<pre>',implode(', ', $this->auth->getErrors()),'</pre>'; | ||
if ($this->auth->getSettings()->isDebugActive()) { | ||
echo '<pre>'.$this->auth->getLastErrorReason().'</pre>'; | ||
exit(); | ||
} | ||
} | ||
} | ||
|
||
public static function get_package_config() { | ||
global $config; | ||
# Only proceed if there are installed packages. Prevents PHP warnings. | ||
if (is_array($config["installedpackages"]["package"])) { | ||
# Loop through each installed package until we find the saml2-auth package | ||
foreach ($config["installedpackages"]["package"] as $id=>$pkg) { | ||
if ($pkg["internal_name"] === "saml2-auth") { | ||
return [$id, $pkg["conf"]]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
public static function get_saml_settings() { | ||
# Local variables | ||
$pkg_conf = SAML2Auth::get_package_config()[1]; | ||
$php_saml_config = array ( | ||
'debug' => boolval($pkg_conf["debug_mode"]), | ||
'sp' => array ( | ||
'entityId' => $pkg_conf["sp_base_url"].'/saml2_auth/sso/metadata/', | ||
'assertionConsumerService' => array ( | ||
'url' => $pkg_conf["sp_base_url"].'/saml2_auth/sso/acs/', | ||
), | ||
'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', | ||
), | ||
'idp' => array ( | ||
'entityId' => $pkg_conf["idp_entity_id"], | ||
'singleSignOnService' => array ( | ||
'url' => $pkg_conf["idp_sign_on_url"], | ||
), | ||
'x509cert' => base64_decode($pkg_conf["idp_x509_cert"]) | ||
), | ||
); | ||
|
||
# When custom parameters are configured, update the config to include them | ||
if (!empty($pkg_conf["custom_conf"])) { | ||
# Decode the custom configuration values. This should be a Base64 encoded JSON string | ||
$custom_conf = json_decode(base64_decode($pkg_conf["custom_conf"]), true); | ||
# Only merge custom configuration in if it decodes to an array | ||
if (is_array($custom_conf)) { | ||
$php_saml_config = array_merge_recursive($php_saml_config, $custom_conf); | ||
} | ||
} | ||
return $php_saml_config; | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.