Статьи

Интеграция Node.js, Ruby и Spring с поддержкой SAML от Okta

Безопасность всегда вызывала у меня интерес, с тех пор, как я впервые разработал  AppFuse  и выяснил, как обеспечить безопасность J2EE еще в 2004 году. Я  взломал AppFuse, чтобы иметь функцию Remember Me , затем перешел на Acegi / Spring Security. Spring Security обладает необходимыми мне функциями, даже если для его настройки требуется почти 100 строк XML. В наши дни это намного лучше, и его JavaConfig — в сочетании с Spring Boot — довольно приятный.

Это была первая часть моей жизни в сфере безопасности. Второй этап начался ночью, когда я встретил Триш и узнал, что она продала продукты безопасности. Она знала об OWASP и их 10 лучших правилах. Именно Триш вдохновила меня на создание  презентации по безопасности веб-приложений на Java . Мне очень понравилось писать эту презентацию, сравнивая платформы безопасности Apache Shiro, Spring Security и Java EE. Я проконтролировал  первый раз, когда представил  несколько  постов и скринкастовХм, может мне стоит обновить презентацию / скринкасты, чтобы использовать только конфигурацию Java ( #NoXML ) и представить ее на пару конференций в этом году?  Я отвлекся.

За последние пару недель мне пришлось сделать всплеск безопасности. Я пытался заставить аутентификацию SAML работать с  Okta  и сервером Active Directory моего клиента. К счастью, кто-то настроил интеграцию AD, поэтому все, что мне нужно было сделать, это попробовать несколько разных языков / сред. Я искал и нашел okta-образцы ThoughtWorks  , которые включают примеры с использованием Node.js и Sinatra (Ruby + JRuby). Я также нашел   пример Spring SAML, который включает одну из моих любимых вещей в JavaLand: конфигурация на основе Java.

Я рад сообщить, что мне удалось заставить все эти приложения работать с настройкой Okta моего клиента. Эта статья расскажет вам, как я это сделал. Для каждого приложения я создал новое приложение в Okta, используя его «Шаблон приложения SAML 2.0», и добавил себя на вкладку «Люди» приложения. Каждый раздел ниже содержит конфигурацию, которую я использовал для Okta. В приведенных ниже инструкциях предполагается, что вы похожи на меня, разработчика, на котором установлены Java 8, Node и Ruby, но нет конкретной платформы. Когда я пишу это, у меня все работает на моем Mac с Yosemite, но я написал инструкции ниже, используя один из моих старых ноутбуков, свежий после обновления Yosemite.

Первым делом я заказал образцы ThoughtWorks.

git clone https://github.com/ThoughtWorksInc/okta-samples.git

Node.js

Я начал с получения примера Node.js. Для конфигурации Okta я использовал:

настройка Стоимость
Метка приложения Пример Okta Node.js
Принудительная аутентификация ложный
URL обратной ссылки HTTP: // локальный: 3000 / Войти / обратный вызов
Имя ID Формат Адрес электронной почты
Получатель HTTP: // локальный: 3000 /
Ограничение аудитории HTTP: // локальный: 3000 /
authnContextClassRef PasswordProtectedTransport
отклик подписанный
Утверждение подписанный
Запрос Сжатый
Назначения HTTP: // локальный: 3000 / Войти / обратный вызов
Заявления об атрибутах электронная почта | $ {user.email}, ПгвЬЫат | $ {user.firstName}

Образец Node.js использует  Express , а также  passport  и  passport-saml . Паспортные пакеты используются для обработки аутентификации SAML, а  connect  — для сжатия запросов с вашего локального сервера.

Единственное, что мне нужно было сделать, чтобы приложение Node.js работало, — это вставить строку сертификата X509 и целевой URL-адрес в  config.json приложение из приложения Okta. В интерфейсе администратора Okta я нажал на вкладку «Вход в систему» ​​и нажал кнопку «Просмотр инструкций по установке». Я скопировал значение «URL перенаправления входа» и скопировал его в значение entryPoint  файла config.json  . Затем я скачал сертификат и открыл его в vi. Я выполнил следующие две команды, чтобы удалить ^ M и окончания строк (  подробнее здесь ).

Затем я скопировал все между  -----BEGIN CERTIFICATE----- и  -----END CERTIFICATE----- и вставить его в  CERT  значения config.json. Мне пришлось удалить комментарии из config.json, чтобы все работало.

После внесения этих изменений я смог запустить «npm install» и «npm start» и успешно войти в систему по адресу http: // localhost: 3000.

Рубин

Образец Ruby использует  Sinatraomniauth  и  omniauth-saml . Чтобы запустить приложение okta-ruby-sinatra, мне пришлось начать с установки Bundler.

sudo gem install bundler

Затем я установил все необходимые гемы для этого проекта, используя следующую команду.

bundle install

Это привело к следующей ошибке:

An error occurred while installing nokogiri (1.6.1), and Bundler cannot continue.
Make sure that `gem install nokogiri -v '1.6.1'` succeeds before bundling

Я попробовал предложение Бандлера, но оно провалилось:

Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby extconf.rb
mkmf.rb can't find header files for ruby at /System/Library/Frameworks/Ruby.
framework/Versions/2.0/usr/lib/ruby/include/ruby.h

Затем я попытался обновить до Xcode 6.1.1. Я получил ту же ошибку, и запуск «bundle update sinatra» и «sudo gem update —system» ничего не помог. Я нашел  старый ответ Stack Overflow, в  котором предлагалось запустить «xcode-select —install» для установки инструментов разработчика командной строки Xcode. После этого я запустил «sudo gcc», чтобы принять все лицензионные соглашения Apple. Я снова запустил «комплектную установку», и на этот раз он завершился ошибкой:

-----
libxml2 is missing. please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.
-----
...
An error occurred while installing nokogiri (1.6.1), and Bundler cannot continue.
Make sure that `gem install nokogiri -v '1.6.1'` succeeds before bundling.

Я снова попробовал предложенное Бандлером: «sudo gem install nokogiri -v ‘1.6.1’». Это не сработало, поэтому я попытался «обновить пакет», и в конце концов это сработало. В последний раз я запустил «пакетную установку», а затем «ruby app.rb». Запустился WEBrick, и я создал приложение Okta Ruby Example на Okta со следующими настройками.

настройка Стоимость
Метка приложения Пример Okta Ruby
Принудительная аутентификация ложный
URL обратной ссылки HTTP: // локальный: 4567 / авториз / SAML / обратный вызов
Имя ID Формат Адрес электронной почты
Получатель HTTP: // локальный: 4567
Ограничение аудитории HTTP: // локальный: 4567
authnContextClassRef PasswordProtectedTransport
отклик подписанный
Утверждение подписанный
Запрос Сжатый
Назначения HTTP: // локальный: 4567 / авториз / SAML / обратный вызов
Заявления об атрибутах электронная почта | $ {user.email}, ПгвЬЫат | $ {user.firstName}

Чтобы настроить Синатру с настройками Отки, я начал с переименования  config.yml.sample файла:

mv config.yml.sample config.yml

В административном интерфейсе Otka для приложения я щелкнул вкладку «Вход в систему» ​​и нажал кнопку «Просмотр инструкций по установке». Я скопировал значение «URL перенаправления входа» и скопировал его в значение target_url  config.yml  . Затем я загрузил сертификат и выполнил следующую команду в каталоге, в который я его скачал.

openssl x509 -noout -fingerprint -in "okta.cert"

Я скопировал отпечатки пальцев в config.yml по  отпечаткам пальцев  значения и перезапустить приложение. Я открыл http: // localhost: 4567 в своем браузере и смог успешно войти в систему.

JRuby

Чтобы начать с JRuby, я сначала прочитал README проекта. В нем упоминаются проблемы с «nokogiri» и объясняется, что проект содержит исправленную версию nokogiri 1.6.0. Поскольку я знал, что существует более поздняя версия, я изменил  Gemfile и удалил информацию о версии и пути из последней строки. Я скопировал  config.yml из проекта Ruby и выполнил следующие команды, чтобы установить Bundler, зависимости проекта и запустить приложение.

jruby -S gem install bundler
jruby -S bundle install

Запуск второй команды привел к следующей ошибке:

Your jruby version is 1.7.18, but your Gemfile specified jruby 1.7.4

Я изменил,  Gemfile чтобы указать «1.7.18» и попытался снова. На этот раз это сработало. Я запустил приложение, используя следующую команду:

jruby app.rb
ПРИМЕЧАНИЕ.  Если вы видите следующее в окне браузера, это означает, что вы забыли скопировать config.yml из проекта Ruby. 

undefined method `auth' for Sinatra::Application:Class

Когда я попытался войти по адресу  http: // localhost: 4567 , я увидел бесконечное перенаправление и следующую ошибку в моей консоли.

W, [2015-01-08T08:53:22.514000 #56144] WARN -- : attack prevented by Rack::Protection::SessionHijacking
0:0:0:0:0:0:0:1 - - [08/Jan/2015 08:53:22] "GET / HTTP/1.1" 302 - 0.0190
0:0:0:0:0:0:0:1 - - [08/Jan/2015:08:53:22 MST] "GET / HTTP/1.1" 302 0

Переполнение стека  указывает на то, что это проблема, вызванная старой версией защиты  стойки . Запуск «jruby -S bundle update rack-protection» обновил проект для использования rack-protection 1.5.3 (был 1.5.1). После перезапуска и повторной попытки я получил следующую ошибку:

I, [2015-01-08T08:59:32.679000 #56176] INFO -- omniauth: (saml) Callback phase initiated.
E, [2015-01-08T08:59:34.747000 #56176] ERROR -- omniauth: (saml) Authentication failure! invalid_ticket: Onelogin::Saml::ValidationError, Digest mismatch
0:0:0:0:0:0:0:1 - - [08/Jan/2015:08:59:34 -0700] "POST /auth/saml/callback HTTP/1.1" 302 9 2.0760
0:0:0:0:0:0:0:1 - - [08/Jan/2015:08:59:34 -0700] "GET /auth/failure?message=invalid_ticket&strategy=saml HTTP/1.1" 404 449 0.0080
0:0:0:0:0:0:0:1 - - [08/Jan/2015:08:59:34 MST] "GET /auth/failure?message=invalid_ticket&strategy=saml HTTP/1.1" 404 449
- -> /auth/failure?message=invalid_ticket&strategy=saml
0:0:0:0:0:0:0:1 - - [08/Jan/2015:08:59:34 -0700] "GET /__sinatra__/404.png HTTP/1.1" 200 18893 0.0200
0:0:0:0:0:0:0:1 - - [08/Jan/2015:08:59:32 MST] "POST /auth/saml/callback HTTP/1.1" 302 9
- -> /auth/saml/callback
0:0:0:0:0:0:0:1 - - [08/Jan/2015:08:59:34 MST] "GET /__sinatra__/404.png HTTP/1.1" 200 18893
http://localhost:4567/auth/failure?message=invalid_ticket&strategy=saml -> /__sinatra__/404.png

На данный момент единственной вещью, отличной от моей рабочей версии и моего старого ноутбука, была версия Java. У моего старого ноутбука была «build 1.8.0_05-b13», поэтому я обновил до последней версии Java 8 (обновление 25). Это не помогло, поэтому я попытался обновить все пакеты с помощью «jruby -S bundle update». Это тоже не удалось, так что я решил попробовать версию JRuby, которая была на моем рабочем ноутбуке (версия 1.7.16.1). Я установил  Homebrew , запустил «brew install jruby», удалил более новую версию из моего пути и понизил версию в  Gemfile. Мне пришлось переустановить Bundler и зависимости проектов с помощью следующих команд.

jruby -S gem install bundler
jruby -S bundle install

Та же ошибка снова. Я вернулся  Gemfile.lock и выполнил единственную команду обновления пакета, которую я запускал на своем рабочем ноутбуке:

$ jruby -S bundle update sinatra

К сожалению, это все еще не решило проблему. Я скопировал проект с моего рабочего ноутбука и попытался запустить этот проект. Это не удалось, доказав, что это проблема среды, а не проекта / кода. Я попытался перезагрузиться, и когда это не сработало, я сдался. Довольно странно, что это не сработало на новой установке Yosemite — мне потребовалось менее 10 минут, чтобы все заработало изначально.

весна

Образцом Spring, с которым я работал в Okta, был образец spring-boot-security-saml-образца Винченцо Де Нотариса  . В этом проекте используются  Spring Boot  и  Spring Security SAML . Я создал приложение «Okta Spring Example» на Okta со следующими настройками.

настройка Стоимость
Метка приложения Okta Spring Пример
Принудительная аутентификация ложный
URL обратной ссылки HTTP: // локальный: 8080 / SAML / SSO
Имя ID Формат Адрес электронной почты
Получатель HTTP: // локальный: 8080 / SAML / SSO
Ограничение аудитории ком: vdenotaris: весна: зр
authnContextClassRef PasswordProtectedTransport
отклик подписанный
Утверждение подписанный
Запрос несжатого
Назначения HTTP: // локальный: 8080 / SAML / SSO
Заявления об атрибутах электронная почта | $ {user.email}, ПгвЬЫат | $ {user.firstName}

Самая большая вещь, которую я узнал, пытаясь получить правильные значения, заключалась в том, что для параметра  Request должно быть установлено значение  Uncompressed .

После клонирования проекта GitHub на жесткий диск я добавил нового поставщика единого входа, добавив новый компонент в  WebSecurityConfig.java. URL-адрес, полученный от интерфейса администратора Okta: Вход> Просмотр инструкций по настройке> Общедоступная ссылка (в нижней части страницы).

@Bean(name = "idp-okta")
public ExtendedMetadataDelegate ssoOktaExtendedMetadataProvider()
      throws MetadataProviderException {
    @SuppressWarnings({ "deprecation"})
    HTTPMetadataProvider httpMetadataProvider
          = new HTTPMetadataProvider("https://client.okta.com/app/random-key-here/sso/saml/metadata", 5000);
    httpMetadataProvider.setParserPool(parserPool());
    ExtendedMetadataDelegate extendedMetadataDelegate =
          new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
    extendedMetadataDelegate.setMetadataTrustCheck(false);
    extendedMetadataDelegate.setMetadataRequireSignature(false);
    return extendedMetadataDelegate;
}

Для работы SSL-соединения мне пришлось скачать сертификат и импортировать его в хранилище ключей приложения. Чтобы сделать это в Chrome, я зашел на https://client.okta.com, щелкнул значок блокировки в адресной строке, а затем перетащил / бросил изображение сертификата на мой рабочий стол. Это привело к  *.okta.com.cer файлу на моем рабочем столе. Я добавил его в хранилище ключей, используя следующие команды (спасибо  Stack Overflow ).

keytool -importcert -file ~/Desktop/\*.okta.com.cer -keystore src/main/resources/saml/samlKeystore.jks

Когда мне предложили ввести пароль, я ввел «nalle123». Это значение указывается в bean- keyManager компоненте WebSecurityConfig.java  . Затем я добавил этого поставщика в список поставщиков в  metadata компоненте.

@Bean
@Qualifier("metadata")
public CachingMetadataManager metadata() throws MetadataProviderException {
        List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
    providers.add(ssoOktaExtendedMetadataProvider());
    providers.add(ssoCircleExtendedMetadataProvider());
    return new CachingMetadataManager(providers);
}

После внесения этих изменений я запустил приложение, используя «mvn spring-boot: run». Я перешел на http: // localhost: 8080, выбрал Okta в качестве моего Idp и успешно вошел в систему!

Резюме

В этой статье показано, как я получил приложения Node.js, Ruby и Spring, работающие с поддержкой SAML от Okta. Мой опыт с этим, когда я впервые попробовал это: Node был очень простым, Ruby был немного сложнее, JRuby был легким, а Spring занял несколько дней. Как вы можете заметить из этой статьи, Ruby / JRuby были самыми трудными для работы на чистой машине.

В целом, работа с Okta до сих пор была приятным опытом. Надеюсь, что эта статья поможет вам получить хороший опыт.