Information for: DEVELOPERS   PARTNERS   SUPPORT

Implementing SimpleSAML on Cloud Next

Note

These changes are only required for SimpleSAMLphp. No action is required for other authentication modules, such as CAS or SAML SP 2.0.

To implement SimpleSAML on Cloud Next, you must:

Setting up SimpleSAMLphp authentication on Cloud Next

To set up SimpleSAMLphp authentication on Drupal 9 or later applications:

Note

For Drupal 7, the same steps work with the exception that Composer isn’t leveraged.

  1. Require the drupal/simplesamlphp_auth project.

    1. Access the repo directory.

      cd repo
      
    2. Install the module with Composer. This installs drupal/externalauth and simplesamlphp/simplesamlphp.

      composer require drupal/simplesamlphp_auth
      
  2. Use symlinks to keep the SimpleSAMLphp configuration and metadata files outside your package. This ensures that you can update the SimpleSAMLphp configuration files through Composer without overwriting your custom configurations.

    1. Create the directories where you want to keep the custom configuration files:

      mkdir -p simplesamlphp_files/config simplesamlphp_files/metadata
      ln -sf ../../../simplesamlphp_files/config vendor/simplesamlphp/simplesamlphp/config
      
    2. Replace any config directory with a symlink to the one created earlier. You must symlnk the config directory because you set the metadata directory as a config setting.

      Note

      To set the configuration, you can define an environment variable: SIMPLESAMLPHP_CONFIG_DIR through Cloud API. Currently, this feature is not available in Cloud Next.

    3. To ensure that the process repeats automatically whenever the project is updated, add the following directive in the composer.json file:

         "scripts": {
              "post-update-cmd": [
                      "rm -rf vendor/simplesamlphp/simplesamlphp/config",
                      "ln -sf ../../../simplesamlphp_files/config vendor/simplesamlphp/simplesamlphp/config"
              ],
              "post-install-cmd": [
                      "rm -rf vendor/simplesamlphp/simplesamlphp/config",
                      "ln -sf ../../../simplesamlphp_files/config vendor/simplesamlphp/simplesamlphp/config"
              ]
      },
      
    4. In your docroot, create a symlink to allow access to the SimpleSAMLphp user interface.

      ln -s ../vendor/simplesamlphp/simplesamlphp/www docroot/simplesaml
      
  3. Create the config.php and authsources.php files.

    1. After creating the directories, create the config.php, authsources.php, and metadata/saml20-idp-remote.php files with the appropriate content.

    2. To create an incomplete metadata/saml20-idp-remote.php file, copy the template:

      cp vendor/simplesamlphp/simplesamlphp/metadata-templates/saml20-idp-remote.php simplesamlphp_files/metadata/saml20-idp-remote.php
      

      Note

      This file is filled with the metadata obtained from your IdP.

    3. To test, copy the authsources.php file:

      cp vendor/simplesamlphp/simplesamlphp/config-templates/authsources.php simplesamlphp_files/config/authsources.php
      
    4. For the config.php file, generate a new hash_salt and an admin password:

      LC_ALL=C tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo
      
      vendor/simplesamlphp/simplesamlphp/bin/pwgen.php
      
    5. Update the following snippet to replace $hashSalt and $adminPassword with the values obtained in the preceding step. In addition, update your Technical Contact name and email.

      <?php
      
         use SimpleSAML\Logger;
      
         $config = [];
      
         /*
         * Perform any global overrides
         */
      
         $config['technicalcontact_name'] = "Your Name";
         $config['technicalcontact_email'] = "[email protected]";
      
         $config['secretsalt'] = $hashSalt;
         $config['auth.adminpassword'] = $adminPassword;
         $config['admin.protectindexpage'] = TRUE;
      
         $samlfiles = __DIR__ . '/../';
         $config['metadatadir'] = $samlfiles . 'metadata/';
         $config['certdir']     = $samlfiles . 'cert/';
      
         /*
         * Perform any local only overrides
         */
         if (file_exists($samlfiles . 'config.local.php')) {
         // Instead of adding all the local configuration, include a file that can be added to .gitignore
         include 'config.local.php';
         }
      
         /*
         * Perform any Acquia Cloud overrides
         */
         if (isset($_ENV['AH_SITE_ENVIRONMENT'])) {
         // do Acquia specific translations here
         // Prevent Varnish from interfering with SimpleSAMLphp.
         // SSL terminated at the ELB / balancer so we correctly set the SERVER_PORT
         // and HTTPS for SimpleSAMLphp baseurl configuration.
         $protocol = 'http://';
         $port = '80';
         if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
             $_SERVER['SERVER_PORT'] = 443;
             $_SERVER['HTTPS'] = 'true';
             $protocol = 'https://';
             $port = $_SERVER['SERVER_PORT'];
         }
         $config['baseurlpath'] = $protocol . $_SERVER['HTTP_HOST'] . ':' . $port . '/simplesaml/';
         $config['trusted.url.domains'] = [$_SERVER['HTTP_HOST']];
      
         // Setup basic file based logging.
         $config['logging.handler'] = 'file';
         // on Cloud Next, the preferred location is /shared/logs
         // on Cloud Classic, the preferred location is the same directory as ACQUIA_HOSTING_DRUPAL_LOG
         $config['loggingdir'] = (file_exists('/shared/logs/'))?'/shared/logs/':dirname(getenv('ACQUIA_HOSTING_DRUPAL_LOG'));
         $config['logging.logfile'] = 'simplesamlphp-' . date('Ymd') . '.log';
      
         // Retrieve database credentials from creds.json
         $creds_json = file_get_contents('/var/www/site-php/' . $_ENV['AH_SITE_GROUP'] . '.' . $_ENV['AH_SITE_ENVIRONMENT'] . '/creds.json');
         $creds = json_decode($creds_json, true);
      
         $database = $creds['databases'][$_ENV['AH_SITE_GROUP']];
         // On Cloud Classic, the current active database host is determined by a DNS lookup
         if (isset($database['db_cluster_id'])) {
             require_once "/usr/share/php/Net/DNS2_wrapper.php";
             try {
             $resolver = new Net_DNS2_Resolver([
                 'nameservers' => [
                 '127.0.0.1',
                 'dns-master',
                 ],
             ]);
             $response = $resolver->query("cluster-{$database['db_cluster_id']}.mysql", 'CNAME');
             $database['host'] = $response->answer[0]->cname;
             }
             catch (Net_DNS2_Exception $e) {
             Logger::warning('DNS entry not found');
             }
         }
         $config['store.type'] = 'sql';
         $config['store.sql.dsn'] = sprintf('mysql:host=%s;port=%s;dbname=%s', $database['host'], $database['port'], $database['name']);
         $config['store.sql.username'] = $database['user'];
         $config['store.sql.password'] = $database['pass'];
         $config['store.sql.prefix'] = 'simplesaml';
      
         }
      

      Note

      SSO providers might fail to connect if you specify the port number as 443. If you see errors, do not specify the port number.

    6. Access your site at the /simplesaml path to view the SimpleSAMLphp interface.

  4. (Optional) register your Service Provider (SP) with your Identity Provider (IdP).

    1. Retrieve the SP’s metadata from: https://<your website>/simplesaml/module.php/saml/sp/metadata.php/default-sp. This is accessible on the Federation tab in the SimpleSAMLphp user interface.
  5. Register your IdP metadata.

    1. Obtain your IdP’s metadata. The metadata is available in the XML format.#. Parse the XML file to an appropriate PHP array. You can leverage the tool available at /simplesaml/admin/metadata-converter.php.
    2. Appended the array to the existing metadata file: simplesamlphp_files/metadata/saml20-idp-remote.php.

Testing the SimpleSAMLphp setup

To test the SimpleSAMLphp setup:

  1. Access the site: https://samltest.id/upload.php.
  2. Register your environment by providing the SP metadata URL.
  3. Click Fetch.
  4. Access https://samltest.id/saml/idp to download the file.
  5. Upload the file to your metadata converter.

Testing in SimpleSAMLphp

You can test the Authentication tab against the default-sp option.

Updating SimpleSAMLphp configuration for Cloud Next

Cloud Classic:

In Cloud Classic, the system parses the config.json or creds.json file for each environment to retrieve the necessary information.

To determine the active primary data server, the platform relies on a local DNS system because of the original failover mechanism in Cloud Classic. If that database is unreachable, the platform updates the DNS entry to note that the other database server must be primary. Prior to the database switch, certain checks occur for replication lag and other factors.

The platform relies on Net_DNS2 to handle the DNS resolution. This helps to determine the currently active database in the cluster.

Cloud Next:

The recent platform enhancements, such as CD environments and Cloud Next, leverages a new database layer that does not necessitate the DNS failover mechanism. Therefore, the Net_DNS2 library is not deployed on the system and the DNS lookup does not return any expected value.

The recommended code snippet for acquia_config.php is updated to check if the environment is a CD environment. This is achieved by parsing the name as CD environments start with ode.

if (substr($_ENV['AH_SITE_ENVIRONMENT'], 0, 3) === 'ode') {

To use SimpleSAMLphp in Cloud Next, you must update the code. You must also ensure that the system checks the configuration of the db_cluster_id value for the database in the config.json object, instead of checking the AH_SITE_ENVIRONMENT string. If the db_cluster_id value is configured, you must use the DNS method. Otherwise, you can use the $databases object.

To achieve this, update the following code:

$databases = json_decode($creds_json, TRUE);
$creds = $databases['databases'][$_ENV['AH_SITE_GROUP']];
if (substr($_ENV['AH_SITE_ENVIRONMENT'], 0, 3) === 'ode') {
    $creds['host'] = key($creds['db_url_ha']);
}
else {
    require_once "/usr/share/php/Net/DNS2_wrapper.php";
    try {
    $resolver = new Net_DNS2_Resolver([
        'nameservers' => [
        '127.0.0.1',
        'dns-master',
        ],
    ]);
    $response = $resolver->query("cluster-{$creds['db_cluster_id']}.mysql", 'CNAME');
    $creds['host'] = $response->answer[0]->cname;
    }
    catch (Net_DNS2_Exception $e) {
    $creds['host'] = "";
    }
}
$config['store.type'] = 'sql';
$config['store.sql.dsn'] = sprintf('mysql:host=%s;port=%s;dbname=%s', $creds['host'], $creds['port'], $creds['name']);
$config['store.sql.username'] = $creds['user'];
$config['store.sql.password'] = $creds['pass'];
$config['store.sql.prefix'] = 'simplesaml';

to

$creds = json_decode($creds_json, true);
$database = $creds['databases'][$_ENV['AH_SITE_GROUP']];
// On Cloud Classic, the current active database host is determined by a DNS lookup
if (isset($database['db_cluster_id'])) {
    require_once "/usr/share/php/Net/DNS2_wrapper.php";
    try {
  $resolver = new Net_DNS2_Resolver([
    'nameservers' => [
      '127.0.0.1',
      'dns-master',
    ],
  ]);
  $response = $resolver->query("cluster-{$database['db_cluster_id']}.mysql", 'CNAME');
  $database['host'] = $response->answer[0]->cname;
}
catch (Net_DNS2_Exception $e) {
        Logger::warning('DNS entry not found');
        }
    }
    $config['store.type'] = 'sql';
    $config['store.sql.dsn'] = sprintf('mysql:host=%s;port=%s;dbname=%s', $database['host'], $database['port'], $database['name']);
    $config['store.sql.username'] = $database['user'];
    $config['store.sql.password'] = $database['pass'];
    $config['store.sql.prefix'] = 'simplesaml';

Logging directories

In Cloud Classic, the logging directory location was /mnt/tmp/<environment>. However, in Cloud Next, the location is /shared/logs/.

Therefore, if you want to straddle Cloud Classic and Cloud Next, update the following snippet:

// Setup basic logging.
$config['logging.handler'] = 'file';
$config['loggingdir'] = dirname(getenv('ACQUIA_HOSTING_DRUPAL_LOG'));
$config['logging.logfile'] = 'simplesamlphp-' . date('Ymd') . '.log';

to

// Setup basic file based logging.
$config['logging.handler'] = 'file';
// on Cloud Next, the preferred location is /shared/logs
// on Cloud Classic, the preferred location is the same directory as ACQUIA_HOSTING_DRUPAL_LOG
$config['loggingdir'] = (file_exists('/shared/logs/'))?'/shared/logs/':dirname(getenv('ACQUIA_HOSTING_DRUPAL_LOG'));
$config['logging.logfile'] = 'simplesamlphp-' . date('Ymd') . '.log';