Билет службы Java GSS-API не сохраняется в кэше учетных данных с использованием Java

Я создал 2 демонстрационных клиента Kerberos, используя GSS-API. Один на Python3, второй на Java. Оба клиента кажутся в целом эквивалентными, и оба «работают» в том смысле, что я получаю служебный билет, который принимается моим субъектом службы Java GSS-API.

Однако при тестировании я заметил, что клиент Python сохраняет билет службы в кеше учетных данных Kerberos, тогда как клиент Java, похоже, не сохраняет билет.

Я использую «klist» для просмотра содержимого кеша учетных данных.

Мои клиенты работают на виртуальной машине Lubuntu 17.04, используя FreeIPA в качестве среды Kerberos. Я использую OpenJDK 8 u131.

Вопрос 1: Java GSS-API не сохраняет билеты службы в кэш учетных данных? Или я могу так изменить свой код?

Вопрос 2: Есть ли обратная сторона того факта, что билет службы не сохраняется в кеше?

Мое предположение состоит в том, что кэшированные служебные билеты уменьшают взаимодействие с KDC, но комментарии к Как сохранить билет службы Kerberos с помощью клиента Windows Java? предполагаем, что это не так, но в техническом примечании Microsoft говорится:« Клиенту не нужно возвращаться к KDC. каждый раз, когда ему нужен доступ к этому конкретному серверу ».

Вопрос 3: Кэшированные билеты службы от клиента python исчезают через несколько минут - задолго до истечения срока их действия. Что заставляет их исчезнуть?

Код Python

#!/usr/bin/python3.5

import gssapi
from io import BytesIO

server_name = 'HTTP/[email protected]'
service_name = gssapi.Name(server_name)

client_ctx = gssapi.SecurityContext(name=service_name, usage='initiate')
initial_client_token = client_ctx.step()

Код Java

System.setProperty("java.security.krb5.conf","/etc/krb5.conf");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");

GSSManager manager = GSSManager.getInstance();
GSSName clientName;
GSSContext context = null;

//try catch removed for brevity
GSSName serverName = 
      manager.createName("HTTP/[email protected]", null);

Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");

//use default credentials
context = manager.createContext(serverName,
    krb5Oid,
    null,
    GSSContext.DEFAULT_LIFETIME);

context.requestMutualAuth(false);
context.requestConf(false);
context.requestInteg(true);

byte[] token = new byte[0];         
token = context.initSecContext(token, 0, token.length);

Изменить:

В то время как исходный вопрос фокусируется на использовании Java GSS-API для создания Java Kerberos Client, GSS не является обязательным. Я открыт для других подходов Kerberos, которые работают на Java. Прямо сейчас я экспериментирую с Apache Kerby curb-client.

Пока что у Java GSS-API есть 2 проблемы:

1) Он использует кеш учетных данных для получения TGT (Ok), но не для кеширования служебных билетов (Not Ok).

2) Он не может получить доступ к кэшам учетных данных типа KEYRING. (Подтверждено поведением, отладкой классов безопасности среды выполнения Java и комментариями в этом коде. Для комбинации Lubuntu / FreeIPA, которую я использую, KEYRING была стандартной настройкой по умолчанию. Это не относится к Windows и может не применяется к другим комбинациям Linux Kerberos.


person FlyingSheep    schedule 04.05.2017    source источник
comment
кстати: я использую GSS без JAAS   -  person FlyingSheep    schedule 04.05.2017
comment
Windows или Linux? В этом разница огромная.   -  person Samson Scharfrichter    schedule 06.05.2017
comment
@SamsonScharfrichter На данный момент Linux. Как я уже сказал в вопросе, клиенты работают на Lubuntu 17.04. И чтобы заставить Java GSS-API вообще использовать кеш учетных данных, мне пришлось переключиться со стандартного KEYRING на FILE. Python GSS был вполне доволен KEYRING.   -  person FlyingSheep    schedule 06.05.2017
comment
Частичный ответ на вопрос 2: Источник Simo в списке рассылки пользователей FreeIPA подтверждает, что, если в кэше учетных данных есть действующий билет службы, клиент не взаимодействует с KDC. Это означает, что если Java GSS-API не может быть настроен для кэширования билетов, KDC будет заблокирован. Нехорошо. Я просканирую свои журналы KDC позже в эти выходные, чтобы доказать / опровергнуть это.   -  person FlyingSheep    schedule 06.05.2017
comment
Опечатка, Simo Sorce (не Источник).   -  person FlyingSheep    schedule 06.05.2017
comment
Сканирование журналов KDC /var/log/krb5kdc.log доказывает, что мой клиент Java GSS-API (как в настоящее время реализован / настроен) забивает KDC с помощью TGS_REQ для каждого вызова службы, в то время как клиент Python GSS-API делает это только один раз ( и использует кэшированный сервисный билет).   -  person FlyingSheep    schedule 06.05.2017
comment
Curb-client Apache Kerby может быть альтернативой GSS github.com/apache/directory-kerby/blob/trunk/docs/1.0.0-rc2/, но я не могу найти примеров   -  person FlyingSheep    schedule 06.05.2017
comment
Lubuntu ... стандартный KEYRING - я впервые слышу, что KEYRING является стандартом для Linux. Вся экосистема Hadoop полагается на ФАЙЛ. Мой клиент использует SSSD для привязки аутентификации Linux (CentOS) к Active Directory и управляет билетами Kerberos через кеш-память FILE.   -  person Samson Scharfrichter    schedule 06.05.2017
comment
Взгляните на этот пост (а также перейдите по ссылке в комментариях), чтобы избавиться от всех иллюзий относительно реализации Kerberos в Java: stackoverflow.com/questions/43660265/   -  person Samson Scharfrichter    schedule 06.05.2017
comment
В документации MIT Kerberos указано, что FILE или KCM может быть значением по умолчанию в зависимости от платформы, но ничего о KEYRING. web.mit.edu/kerberos/krb5-1.15/doc/ basic / ccache_def.html   -  person Samson Scharfrichter    schedule 06.05.2017
comment
@SamsonScharfrichter, для стандартного чтения по умолчанию в моей клиентской комбинации Lubuntu / FreeIPA. Изменение на ФАЙЛ позволило GSS найти TGT (подтверждено отладкой классов безопасности Java RT), но билет службы не кэшируется. Придется сделать еще отладку RT ...   -  person FlyingSheep    schedule 06.05.2017


Ответы (1)


Изменить 2:

Мне следовало задать следующий вопрос:

Как сделать так, чтобы мой KDC не зависал от повторных запросов SGT, потому что Java GSS не использует кеш учетных данных.

Я оставляю свой исходный ответ внизу, потому что он в основном сосредоточен на исходном вопросе.

После очередного раунда глубокой отладки и тестирования я нашел приемлемое решение основной проблемы.

Использование Java GSS API с JAAS, в отличие от "чистого" GSS без JAAS в моем исходном решении, имеет большое значение!

Да, существующие сервисные билеты (SGT), которые могут находиться в кэше учетных данных, не загружаются, и никакие вновь приобретенные SGT не записываются обратно в кэш, однако KDC не подвергается постоянному воздействию (настоящая проблема).

И чистый GSS, и GSS с JAAS используют основную тему клиента. У субъекта есть набор privateCredentials в памяти, который используется для хранения TGT и SGT.

Ключевое отличие:

  • «чистый GSS»: объект + privateCredentials создается в GSSContext и живет только до тех пор, пока живет GSSContext.

  • GSS с JAAS: объект создается JAAS вне GSSContext и, таким образом, может существовать в течение всего жизненного цикла приложения, охватывая множество GSSContexts в течение всего жизненного цикла приложения.

Первый установленный GSSContext будет запрашивать личные учетные данные субъекта для SGT, но не находить его, а затем запрашивать SGT от KDC.

SGT добавляется к privateCredentials субъекта, и, поскольку субъект живет дольше, чем GSSContext, он доступен, как и SGT, при создании следующих GSSContexts. Они найдут SGT в личных учетных данных субъекта, и им не нужно нажимать KDC для нового SGT.

Итак, в свете моего конкретного Java Fat Client, открытого один раз и, вероятно, работающего часами, все в порядке. Первый созданный GSSContext попадет в KDC для SGT, который затем будет использоваться всеми последующими созданными GSSContexts, пока клиент не будет закрыт. Кэш учетных данных не используется, но это не повредит.

В свете гораздо более короткоживущего клиента, который открывался много раз и, возможно, параллельно, использование / неиспользование кеша учетных данных может быть более серьезной проблемой.

private void initJAASandGSS() {
    LoginContext loginContext = null;               
    TextCallbackHandler cbHandler = new TextCallbackHandler();
    try {
        loginContext = new LoginContext("wSOXClientGSSJAASLogin", cbHandler);
        loginContext.login();
        mySubject = loginContext.getSubject();
    } catch (LoginException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    gssManager = GSSManager.getInstance();

    try {
        //TODO: LAMB: This name should be got from config / built from config / serviceIdentifier
        serverName = gssManager.createName("HTTP/[email protected]", null);
        Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
    } catch (GSSException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();        
    }
}

private String getGSSwJAASServiceToken()  {

    byte[] token = null;
    String encodedToken = null;

    token = Subject.doAs(mySubject, new PrivilegedAction<byte[]>(){
        public byte[] run(){
            try{

                System.setProperty("javax.security.auth.useSubjectCredsOnly","true");
                GSSContext context = gssManager.createContext(serverName,
                    krb5Oid,
                    null,
                    GSSContext.DEFAULT_LIFETIME);

                context.requestMutualAuth(false);
                context.requestConf(false);
                context.requestInteg(true);

                byte[] ret = new byte[0];           
                ret = context.initSecContext(ret, 0, ret.length);

                context.dispose();

                return ret;

            } catch(Exception e){
                Log.log(Log.ERROR, e);
                throw new otms.util.OTMSRuntimeException("Start Client (Kerberos) failed, cause: " + e.getMessage());
            }
        }
    });

    encodedToken = Base64.getEncoder().encodeToString(token);
    return encodedToken;
}

Конец редактирования 2: исходный ответ ниже:

Вопрос 1: Java GSS-API не сохраняет билеты службы в кэш учетных данных? Или я могу так изменить свой код?

Изменить: анализ первопричин.

После многих часов отладки классов sun.security. * Я теперь понимаю, что код GSS и Java Security делает / не делает - по крайней мере, в Java 8 u 131.

В этом примере у нас есть кеш учетных данных типа, к которому GSS может получить доступ, содержащий действительный билет предоставления билета (TGT) и действительный билет службы (SGT).

1) Когда создается субъект-клиент-участник, TGT загружается из кеша (Credentials.acquireTGTFromCache ()) и сохраняется в наборе privateCredentials субъекта. -> (ОК)

Загружается только TGT, SGT НЕ загружаются и не сохраняются в Subject privateCredentials. -> (НЕ ОК)

2) Позже, глубоко в процессе GSSContext.initSecContext (), код безопасности фактически пытается получить служебный билет из privateCredentials субъекта. Соответствующий код - Krb5Context.initSecContext () / KrbUtils.getTicket () / SubjectComber.find () / findAux (). Однако, поскольку SGT никогда не загружались на шаге 1) SGT не будет найден! Поэтому новый SGT запрашивается у KDC и используется.

Это повторяется для каждого запроса на обслуживание.

Просто для удовольствия и строго в качестве хака для проверки концепции, я добавил несколько строк кода между логином и initSecContext (), чтобы проанализировать кеш учетных данных, извлечь учетные данные, преобразовать их в учетные данные Krb и добавить их в личные учетные данные Субъекта.

Это сделано на шаге 2) существующий SGT найден и используется. Никаких новых SGT от KDC не запрашивается.

Я не буду публиковать код этого взлома, поскольку он вызывает внутренние классы sun, которые мы не должны вызывать, и я не хочу никого вдохновлять на это. Я также не собираюсь использовать этот хак в качестве решения.

-> Основная причина проблемы заключается не в том, что билет службы не СОХРАНЕН в кэш; скорее

a) что SGT не ЗАГРУЖАЕТСЯ из кэша учетных данных в Субъект клиента-участника.

а также

б) что для этого нет общедоступного API или настроек конфигурации.

Это влияет на GSS-API как с JAAS, так и без него.

Так что же мне остается?

i) Используйте Java GSS-API / GSS-API с JAAS «как есть», при этом каждый запрос SGT попадает в KDC -> Плохо.

ii) Как предлагает Самсон в комментариях ниже, используйте Java GSS-API только для первоначального входа в приложение, затем для всех дальнейших вызовов используйте альтернативный механизм безопасности для последующих вызовов (своего рода самодельный kerberos-light) с использованием токенов. или куки.

iii) Рассмотрите альтернативы GSS-API, такие как Apache Kerby curb-client. Это имеет значение, выходящее за рамки этого ответа, и вполне может оказаться прыжком с пресловутой сковороды на огонь.

Я отправил Oracle Feature Request Java, предлагая, чтобы SGT были извлечены из кеша и сохранены в учетных данных Subject (как уже было в случае с TGT).

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180144

Вопрос 2: Есть ли обратная сторона того факта, что билет службы не сохраняется в кеше?

Использование кеша учетных данных для сервисных билетов сокращает взаимодействие между клиентом и KDC. Следствием этого является то, что там, где служебные билеты не кэшируются, каждый запрос потребует взаимодействия с KDC, что может привести к тому, что KDC будет заблокирован.

person FlyingSheep    schedule 07.05.2017
comment
Как бы то ни было, экосистема Hadoop использует служебные билеты только один раз для генерации своего рода токена аутентификации, например хэш MD5 для связи RPC или подписанный файл cookie для REST и GUI, который используется до тех пор, пока не истечет срок его действия и не потребуется еще один раунд аутентификации Kerberos. Официальная причина заключается в том, что Kerberos не был разработан для распределенных систем, поэтому аутентификация с использованием 1 службы на N хостах действительно обременительна. Но теперь я думаю, что отсутствие поддержки Java для кэширования служебных билетов также было хорошим стимулом. - person Samson Scharfrichter; 08.05.2017
comment
Отвечая на ваш вопрос: Использование кеша для сервисных билетов снижает взаимодействие между клиентом и KDC ›› всегда ли это один и тот же клиент, то есть служба, или просто приложение, выполняющее несколько вызовов последовательно? Если да, то каковы преимущества общего кеша по сравнению с простым управлением частной коллекцией context объектов? - person Samson Scharfrichter; 08.05.2017
comment
Ваш код неверен, как и код @FlyingSheep. Вы никогда не завершите цикл аутентификации. Ребята, не пытайтесь это делать на работе. - person Michael-O; 12.05.2017
comment
@FlyingSheep, большое спасибо, действительно очень полезный ответ. - person Fabiano Tarlao; 17.08.2017