Grafana: Автоматический вход по URL-адресу токена

Созданный на 15 янв. 2016  ·  95Комментарии  ·  Источник: grafana/grafana

Было бы неплохо иметь автоматический вход в систему, передающий токен пользователя через URL-адрес, это могло бы быть частичным решением для встраивания фреймов на сайты.

arebackenauth help wanted prioritimportant-longterm prioritunscheduled typfeature-request

Самый полезный комментарий

Это очень важно для взаимодействия с SaaS.
У нас есть собственная аутентификация и информационные панели, мы хотим предоставить возможность передавать пользователя с нашей приборной панели в Grafana - аналогично тому, как Heroku взаимодействует с New Relic и другими сервисами с нулевым паролем.

Предоставление метода аутентификации url + токена является обычным делом.
Если кто-то затем вставит это на общедоступный веб-сайт, никакая безопасность не спасет этого человека от себя. Это их вина.
Для этого подойдет достаточно предупреждений в документации или пользовательском интерфейсе.

Без этой функции пользователи должны столкнуться со вторым экраном входа в систему, где они не смогут понять, какое имя пользователя и пароль нужно ввести.

Для нас это большой блокиратор.

Все 95 Комментарий

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

Да, но при использовании снимков у вас нет разрешения на их редактирование, или есть ли способ редактировать панель управления с помощью снимков?

Это очень важно для взаимодействия с SaaS.
У нас есть собственная аутентификация и информационные панели, мы хотим предоставить возможность передавать пользователя с нашей приборной панели в Grafana - аналогично тому, как Heroku взаимодействует с New Relic и другими сервисами с нулевым паролем.

Предоставление метода аутентификации url + токена является обычным делом.
Если кто-то затем вставит это на общедоступный веб-сайт, никакая безопасность не спасет этого человека от себя. Это их вина.
Для этого подойдет достаточно предупреждений в документации или пользовательском интерфейсе.

Без этой функции пользователи должны столкнуться со вторым экраном входа в систему, где они не смогут понять, какое имя пользователя и пароль нужно ввести.

Для нас это большой блокиратор.

Похоже, хорошая функция в Grafana. Хотел бы помочь с PR, потому что сейчас очень много других вещей, которые действительно важны

Красивое объяснение

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

+10 за это !!
Чтобы сделать это безопасным, в токене должен быть хэш имени пользователя + пароль + дата. Это может быть использовано для истечения срока действия токена через x часов для повышения безопасности.

Если у меня будет время, я серьезно подумаю над этим

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

@adamlwgriffiths , это также должно поддерживать некоторые основные роли ... такие как просмотрщик, редактор ... и т.д., аналогично тому, что делается с ключами API ... Возможно, мы могли бы просто расширить это, чтобы мы могли передавать ключ api через URL-адрес?

Я думаю, что в долгосрочной перспективе необходимо консолидировать токен user / org / api, а не добавлять новый метод.
Если бы токены API были расширены, чтобы разрешить использование из интерфейса HTML, это не было бы проблемой.
У токенов API уже есть роли, и их можно отозвать, поэтому их просто нужно использовать в веб-представлении, а не в чистом API.

Токены IMO API должны работать так же, как и обычные пользователи. Существует произвольная сегрегация, которая не задокументирована, и я считаю ее противоречащей интуиции. Я думаю, что лучше всего было бы включить все проверки входа, чтобы также проверять ключи API.

Была ли реализована эта функция? Я пытался настроить автоматическое перенаправление пользователя на панель управления Grafana с моего сайта.

@comcomservices В продолжение самого токена вы определенно можете зашифровать информацию внутри токена с помощью идентификатора пользователя и срока действия токена. Таким образом, вы можете просто прочитать сам токен и получить информацию, не требуя, чтобы токены хранились в базе данных и выполняли «очистку и очистку» над старыми токенами.

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

Другая проблема с зашифрованными токенами заключается в том, что они довольно большие, и чем больше информации, тем больше они становятся.
Где, как случайная строка, может быть любой длины, если пространство достаточно велико, чтобы сделать грубую силу невозможной.

@adamlwgriffiths, что касается криптографии, у меня есть формальный опыт в этом, и мне это нравится, но в этом случае я полностью согласен с тем, что токен был бы лучше всего ... Просто пытаюсь разобраться в этом, делаю c ++ и asm слишком долго ... Я получу это, и это первый в моем списке

Я с нетерпением жду этой возможности! Что касается комментария raven-js от Sentry ; он сделает общедоступный URL-адрес, а затем ограничит его по происхождению (см. ниже). Снизит ли это проблему безопасности?

screen shot 2016-03-24 at 10 57 18 am

@ Germanaz0 общедоступен ли ваш JS для автоматического входа в систему? Я хочу реализовать что-то подобное с основной веткой, и кажется, что бэкэнд перенаправляется на экран входа в систему до перехода на страницу панели инструментов. Я еще не очень хорошо знаком со структурой проекта, поэтому, возможно, я не подключаюсь к нужному разделу в коде JS.

@rca да, но мое решение очень простое https://gist.github.com/Germanaz0/d41b5f60dd8097405b6b

Вы получаете входной параметр типа? T = base64 json с {user: _USERNAME_, pass: _PASS_, redirect_to: _URL_TO_REDIRECT}

Вы должны включить этот сценарий на страницу входа в систему.

@ Germanaz0 , спасибо, что поделились! Отправив свой комментарий, я начал смотреть и, как вы отметили, обнаружил, что мне нужно обновить страницу входа. Я применил немного другой подход и обновил контроллер входа в систему вместо того, чтобы делать отдельный скрипт; мое изменение здесь: https://github.com/zymbit/grafana/tree/auto-login-by-cookie-redirect

Это достойно запроса на перенос? Я был бы счастлив сделать необходимые модификации, чтобы перевести это в состояние слияния.

Есть новости по этому поводу?
@rca Вы создали пиар?

+1 за пиар от @rca

@ Germanaz0 , в какой файл вы включаете свой .js файл?

@rca у вас есть какие-нибудь инструкции по использованию вашего мода контроллера входа в систему? Я новичок в графане и пытаюсь заставить это работать для автоматического киоска.

Кстати, если кому-то еще нужен способ сделать это для киосков и т. Д., Проверьте authproxy Grafana . Я смог выполнить то, что мне нужно, с этим и apache.

@scottfuhrman Ничего официально не написано, однако вот мой вариант использования и реализация:

Я искал не режим киоска, а способ встроить панель управления как iframe на внешнюю страницу. Графики встроены довольно чисто, например:

screen shot 2016-12-14 at 10 24 06 am

На странице, которую вы хотите встроить в панель управления, вам потребуется следующая разметка:

<div id='grafana-dashboard' class="col-lg-12"></div>


<script type="text/javascript">
    GrafanaEmbed = {
        grafanaUrl: 'https://your.grafana.example.com',
        dashboard: 'dashboad-name',
        queryParams: {
            dashnav: 0,
            // this is a base64-encoded string of username:password
            // for example on a *NIX machine (and Mac OS X):
            // $ echo "kiosk1:supersecret" | base64
            // a2lvc2sxOnN1cGVyc2VjcmV0Cg==
            auth: 'a2lvc2sxOnN1cGVyc2VjcmV0Cg==',
            theme: 'light'
        }
    };

    (function() {
        var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
        d.src = GrafanaEmbed.grafanaUrl + '/public/app/features/dashboard/embed.js';
        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
    })();
</script>

Надеюсь это поможет.

+1

У меня нет кадровых ресурсов для этого, но я был бы рад собрать средства для этого.

Думал, что решил эту проблему, пока не попытался использовать плейлист. Использование authproxy работает для данной панели инструментов, но как только я попробовал список воспроизведения с несколькими панелями мониторинга, столкнулся с той же проблемой. Я не понимаю смысла использования режима киоска, когда невозможно реализовать настоящее киоск-приложение, если мы полностью не отключим безопасность просмотра?

@torkelo Я нанял @ over64 для работы над этим для меня.

Как вы рекомендуете это реализовать? В основном мне нужно иметь токен в строке URL-адреса, который обходит / решает аутентификацию. Какой лучший подход для этого?

не уверен, пришлось бы исследовать, как такая функция должна быть реализована безопасно

Понял. Мы готовы, как только вы придумаете план :)

если у вас есть идея, как это сделать, дайте мне знать, и я смогу ее оценить

Хорошо. @ over64 взглянет и что-нибудь предложит.

@torkelo Можете ли вы посмотреть на PR @ over64 в # 7431, чтобы понять, имеет ли это смысл.

Любой простой обходной путь? Я пытаюсь реализовать решение @ Germanaz0, но оно у меня не работает :(

Это необходимо, иначе он заблокирован при встраивании снимка в iframe. URL-адрес встроенного снимка должен поддерживать ключ / токен входа.

Всем привет,

Последние комментарии в этой ветке касаются:

  • безопасность
  • встраивание в iframe

Что касается безопасности, обратите внимание на мой старый комментарий выше: https://github.com/grafana/grafana/issues/3752#issuecomment -200874453

А что касается встраивания в iframe: https://github.com/grafana/grafana/issues/3752#issuecomment -267062843

Эта ветка (хотя и довольно старая) содержит код, заимствованный из Discourse, который точно определяет размер iframe, а также аутентифицируется с помощью токена, как описано в комментарии выше.

@torkelo Пожалуйста, дайте мне знать, если вы рассматриваете запрос на

@rca, что это за embed.js в приведенном выше коде?

@zoell - это некоторые js, заимствованные из проекта Discourse, чтобы правильно изменить размер iframe на странице на основе встроенного содержимого с фреймом.

@rca о, спасибо. Мне не удалось найти этот файл в указанной вами ветке. Это доступно где-нибудь еще?

@zoell , я разветвлю это репо и

@rca, спасибо, это было бы здорово.

@rca Извините, я до сих пор не могу найти файл в упомянутой вами ветке.

@TinaRen @zoell Я упал, извиняюсь.

Однако, взглянув только что, похоже, что файл был там все время! 😬

Я буду проделывать дополнительную работу над этим по адресу:

https://github.com/rca/grafana

И файл находится по адресу:

https://github.com/rca/grafana/blob/embedding/public/app/features/dashboard/embed.js

Если этот файл не создается и не попадает в каталог public_gen/ , сообщите мне.

Спасибо!

Можно ли хотя бы ограничить графану доступами к localhost? (чтобы ограничить совместное использование панели одним сервером)

Я хотел бы, чтобы кто-нибудь попробовал это сделать с помощью правильной реализации, которая могла бы пойти вверх по течению. Предложенное нами решение (# 7431) так и не было принято :(

Я рад, что кто-то с нашей стороны потратит на это немного времени, но нам понадобится руководство о том, что будет приемлемым в восходящей сети.

Удалось сделать это с успехом, используя auth.proxy Grafana и ngx_http_secure_link_module Nginx.

Ссылки, которые я использую, имеют формат http://grafana/?user=nayar&md5=2tutcea9nfdsfdsfdsw&expires=1505386800

Как только пользователь нажимает на нее, устанавливается cookie сеанса, и пользователь может просматривать графану, как если бы он вошел в систему в обычном режиме.

Преимущества:

  • Ссылка истекает через некоторое время + безопасность
  • Ссылка генерирует хеш с идентификатором пользователя, отметкой времени и ключом доступа + безопасность
  • Не нужно вводить пароль + удобство

Моя конфигурация nginx выглядит так

server {
    listen 3001 default_server;
#   listen [::]:3001 default_server;

    server_name _;

        location / {
        set $user "";
        set $state "";

        if ($args ~ "^user=(.+)&md5") {
                    set $user $1;
                    set $state "${state}U";
                }

        secure_link $arg_md5,$arg_expires;
                secure_link_md5 "$secure_link_expires$uri$user grafanarocks";

                if ($secure_link = "") { 
                    set $state "${state}S1";
                }

                if ($secure_link = "0") { 
                    set $state "${state}S2";
                }

                add_header X-uristate "$state";

                if ($state = "US1") { return 403; }
                if ($state = "US2") { return 410; }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://127.0.0.1:3000;
            }
    }

Не стесняйтесь обращаться ко мне, если вам понадобится помощь с этим

Очень интересно, @Nayar. Однако меня беспокоит то, что это решение требует довольно большого количества настроек. Я бы предпочел что-то, что изначально встроено в Grafana.

Я использовал стоковую Grafana. Просто пришлось перекомпилировать стандартный nginx с включенным модулем и все.

Наяр, не могли бы вы сообщить мне, может ли реализованное вами решение работать со всеми версиями grafana.я я новичок в Grafana. В моей организации уже установили

Высказывание некоторых идей (и проблем) о том, как это решить.

Самое быстрое решение:

1) добавьте специальный тип (флаг) в ключ API, чтобы его можно было использовать в URL-адресе для входа в систему.

Плюсы: ключи API могут быть добавлены только администраторами организации.


2) дать пользователям возможность создавать ключи API и ключи URL (вариант ключа API).

Проблемы с этим будут связаны с настройками oauth, где, если пользователь может создать ключ api или URL-адрес, он сможет использовать его после того, как им будет удален доступ из Grafana (из системы oauth). Поскольку ключ api или URL-адрес не требует входа в систему по oauth. Это будет большой проблемой для пользовательских ключей api, когда мы в конечном итоге их сделаем. Одним из решений по смягчению последствий может быть разрешение создавать ключи API пользователей только администраторам организации или администраторам сервера Grafana.

Мысли @DanCech ? Продолжить использование ключа API с флагом AllowUrlLogin?

Я не думаю, что использование ключевой инфраструктуры API - это правильный путь, поскольку это затрудняет добавление дополнительных функций позже (например, ограничение URL-адреса для определенного набора панелей мониторинга, что позволяет не администраторам создавать ссылки для входа ) и может сбить с толку пользователей. Мы, конечно, могли бы использовать те же концепции и иметь аналогичную внутреннюю структуру, но это даст нам больше возможностей, если мы их разделим, и я думаю, что это будет более понятно пользователям.

Что касается вопроса входа в систему oauth, я думаю, мы должны выяснить, какой объем должен быть здесь. Та же проблема существует для пользователей oauth, если у вас также включены пароли, поскольку они могут установить локальный пароль и смогут входить таким образом после отключения учетной записи oauth. Единственное реальное решение этой проблемы - сохранить эту ссылку в учетной записи пользователя и всегда отправлять пользователя oauth через цикл перенаправления oauth перед принятием его входа в систему.

Я также считаю, что нам нужно придумать разумное название для этой функции. «URL-адрес входа» может быть слишком широким и на самом деле не оставляет места для использования в будущем как способ, например, поделиться действующей ссылкой, ограниченной определенной панелью управления.

@mattttt хотел бы узнать ваши мысли

поскольку это затрудняет добавление дополнительных функций позже (например, ограничение URL-адреса для определенного набора панелей мониторинга, что позволяет пользователям, не являющимся администраторами, создавать ссылки для входа)

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

Единственное реальное решение этой проблемы - сохранить эту ссылку в учетной записи пользователя и всегда отправлять пользователя oauth через цикл перенаправления oauth перед принятием его входа в систему.

Не уверен, как это решает проблему «отзыва» URL-адреса входа / токена API пользователя. Если они никогда не попытаются войти в систему с помощью oauth после того, как их доступ был удален, Grafana никогда не узнает.

@hemsush Согласно этому сообщению в блоге, он должен работать начиная с Grafana 2.0 и выше: https://grafana.com/blog/2015/12/07/grafana-authproxy-have-it-your-way/

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

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

Не уверен, как это решает проблему «отзыва» URL-адреса входа / токена API пользователя. Если они никогда не попытаются войти в систему с помощью oauth после того, как их доступ был удален, Grafana никогда не узнает.

Я считаю, что представление, которое получает клиент при использовании одной из этих ссылок, должно быть похоже на «анонимный» пользователь, когда он не входит в систему как определенный пользователь волшебным образом, а скорее получает ограниченную учетную запись Viewer, которая полностью отменяет эту функцию из индивидуальных учетных записей пользователей. Это позволит обойти проблему разрешений отдельных учетных записей пользователей, снизить уровень риска, поскольку ссылка не может использоваться для внесения каких-либо изменений, и позволит централизованно управлять всеми активными ссылками доступа администраторами организации.

Предложение решения

1) Представьте новую концепцию «Токен URL-адреса средства просмотра», которую вы можете добавить / удалить со страницы ключей API (мы можем создать новую страницу для этого позже). Сохраните в новой таблице url_token, и они будут работать с точки зрения безопасности, очень похожими на ключи API. То есть они будут генерироваться и проверяться так же, как ключи API. Однако его можно использовать для входа в систему через использование в url с помощью "& url-auth-token =".
2) Возможность ограничить токен для работы только с API рендеринга PNG (будет более безопасным, поскольку пользователи с токеном URL не смогут отправлять запросы только для просмотра существующих панелей / панелей)
3) В будущем нам нужно будет найти способ привязать токен к группам пользователей и разрешениям панели инструментов, пока не знаю, как это будет работать. Я думаю, было бы неплохо создать для этого фиктивного пользователя, который можно было бы использовать для предоставления пользователям «токена URL» прав доступа к определенным панелям мониторинга и источникам данных. Не хотелось бы иметь явные проверки / соединения для токенов URL в проверках разрешений.

4) Дайте понять, что любой, у кого есть токен url, будет иметь доступ ко всем источникам данных организации (и может технически выдавать любой запрос) (если не используется параметр ограничения на рендеринг api).

мысли @bergquist @DanCech

Возможность ограничить токен для работы только с API рендеринга PNG (будет более безопасным, поскольку пользователи с токеном URL не смогут отправлять запросы только для просмотра существующих панелей / панелей мониторинга)

Я не думаю, что рендеринг PNG - хорошее решение. Он будет мерцать и выглядеть не так красиво, как настоящая Графана.

Мне нравится идея подключить токен к пользователю. Самый простой способ подключить этот токен / логин к группам пользователей и разрешениям папки панели инструментов. Но я думаю, что вход в систему с использованием токена (вход в систему / режим просмотра / режим ТВ) должен заставить пользователя быть в роли Viewer .

Дайте понять, что любой, у кого есть токен url, будет иметь доступ ко всем источникам данных организации (и может технически выдавать любой запрос) (если не используется параметр api restrict to render).

Может помочь добавление информации и предупреждений при создании новых токенов.

Проблема с привязкой токена к «нормальному» пользователю заключается в том, что это вызывает всевозможные проблемы, когда пользователь изменяется или удаляется. Если токены URL-адресов могут быть членами группы так же, как и пользователи, тогда будет огромная гибкость в настройке отдельных URL-адресов с доступом к определенным панелям мониторинга и т. Д.

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

Что касается фактического обеспечения доступа к источникам данных, здесь нет разницы, чем с обычными пользователями. Конечное решение этой проблемы состоит в том, чтобы переместить плагины источника данных в бэкэнд и получить запросы к источнику данных для отправки через интерфейс, указав значения переменных панели, панели, временного диапазона и шаблона и создав фактические запросы на бэкэнд после проверки того, что пользователь имеет доступ и что значения переменной шаблона допустимы.

Проблема с привязкой токена к «нормальному» пользователю заключается в том, что это вызывает всевозможные проблемы, когда пользователь изменяется или удаляется.

Проблема с тем, что любой, кто знает токен (даже если учетная запись отключена) может просматривать информационные панели, верна для обоих решений. Отмена доступа может быть произведена путем удаления пользователя или токена.

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

Проверки должны выглядеть одинаково независимо от аутентификации. Так что это не будет большой проблемой. Пользовательский интерфейс для добавления токенов / пользователей в группы, информационные панели и т. Д. Может быть беспорядочным и поможет только очень небольшому количеству пользователей.

Что касается фактического обеспечения доступа к источникам данных, здесь нет разницы, чем с обычными пользователями. Конечное решение этой проблемы состоит в том, чтобы переместить плагины источника данных в бэкэнд и получить запросы к источнику данных для отправки через интерфейс, указав значения переменных панели, панели, временного диапазона и шаблона и создав фактические запросы на бэкэнд после проверки того, что пользователь имеет доступ и что значения переменной шаблона допустимы.

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

Я также столкнулся с этой проблемой с помощью встроенной панели инструментов в режиме username+passwd+ldap auth ...
Есть новости об этом?

Меня очень интересует опция токена зрителя. Если я могу чем-то помочь, я буду рад помочь.

Привет ребята!
Я пытаюсь создать аутентифицированную ссылку для дашборда, у меня есть бэкэнд в java и интерфейс в JSF, я хочу поместить пользователя на экран, который при нажатии перенаправляет пользователя непосредственно на его дашборд, я Я плохо понимаю концепцию, кто-то. У вас есть пример того, как я могу это сделать?

Здравствуй,

Просто идея, пытаюсь помочь. Почему бы не добавить новый пользовательский API, используя базовую аутентификацию, как это
? curl https://admin:admin<strong i="7">@localhost</strong>:3000/api/user/cookie
и получите результат JSON, например
{"user_name":"admin","cookie_name":"grafana_,session":"a0b1c2d3e4","remember":"da27ef425e9e0d"}

Он будет защищен через https, и я мог бы использовать эту информацию, чтобы подделывать файлы cookie и разрешать пользователям просматривать их красивые диаграммы.

Я внимательно смотрю на ваш код и думаю ( очень уважительно ), что это не будет слишком сложно.
Вместо отправки:
user.Rands+user.Password, setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/"

в функцию SetSuperSecureCookie, вы можете создать новую функцию, вдохновленную SetSuperSecureCookie, примерно так:

func (ctx *Context) NewFunc(secret, name, value string, others ...interface{}) {
   key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
   text, err := com.AESGCMEncrypt(key, []byte(value))
   return hex.EncodeToString(text)
}

Ответ может быть подробным и отправлен как в одной из ваших функций, например:

func getUserUserProfile(userId int64) Response {
    query := m.GetUserProfileQuery{UserId: userId}

    if err := bus.Dispatch(&query); err != nil {
        return ApiError(500, "Failed to get user", err)
    }

    cook:= array
    result := {
        user_name: user.name,
                cookie_name:   cookie.name,
        session: s.session,
                remember: cokie.remember

    }

    return Json(200, &result)
}

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

Во время GrafanaCon @DanCech упомянул идею создания публичных плейлистов с ключом GUID. Подобно тому, как работают снимки сегодня. Совместное использование / хранение такого ключа можно рассматривать как безопасное использование токена API, но ограничивается просмотром списков воспроизведения. Список воспроизведения может быть связан с создателем / средством обновления для проверки разрешений на просмотр панели управления.

URL-адрес может выглядеть примерно так: https://play.grafana.com/playlists/public/<hash>

Что мне нравится в этом решении, так это то, что оно ограничивает функциональность только просмотром плейлистов (приборной панели), что, на мой взгляд, является наиболее распространенным вариантом использования таких токенов.

Но нам все равно нужно будет как-то войти в систему, так как acl / аннотации панели инструментов и т. Д. Потребуют аутентификации на стороне сервера.

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

Спасибо @bergquist , определенно хорошо записать этот материал, пока он еще свежий!

Следующим этапом развития этой мысли было добавление концепции «экрана» для дальнейшего разделения вещей, чтобы пользователь мог создать «экран», который затем имел секретный хешированный URL-адрес, к которому он мог подключаться. Это будет служить той же цели, но позволит управлять экранами из Grafana, чтобы пользователь мог контролировать, какой список воспроизведения отображается и т. Д.

Конечная цель здесь заключалась бы в поддержке механизма, с помощью которого пользователь мог бы легко создать SD-карту или образ USB, который можно было бы использовать для загрузки выделенного Raspberry Pi и безопасного отображения желаемого списка воспроизведения из Grafana без необходимости перепрыгивать через кольца аутентификации.

Конечная цель здесь заключалась бы в поддержке механизма, с помощью которого пользователь мог бы легко создать SD-карту или образ USB, который можно было бы использовать для загрузки выделенного Raspberry Pi и безопасного отображения желаемого списка воспроизведения из Grafana без необходимости перепрыгивать через кольца аутентификации.

@DanCech Это именно тот вариант использования, который я ищу.

Следующим этапом развития этой мысли было добавление концепции «экрана» для дальнейшего разделения вещей, чтобы пользователь мог создать «экран», который затем имел секретный хешированный URL-адрес, к которому он мог подключаться.

@DanCech Звучит идеально!

Всем привет,

Кажется, это немного далеко от исходного выпуска, открытого Germanaz0.

Было бы неплохо иметь автоматический вход в систему, передающий токен пользователя через URL-адрес, это могло бы быть частичным решением для встраивания фреймов на сайты.

и объяснил adamlwgriffiths

У нас есть собственная аутентификация и информационные панели, мы хотим предоставить возможность передавать пользователя с нашей приборной панели в Grafana - аналогично тому, как Heroku взаимодействует с New Relic и другими сервисами с нулевым паролем. Предоставление метода аутентификации url + токена является обычным делом.

Germanaz0 разрабатывает js-скрипт

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

Я понимаю, что им нужен автоматический вход, чтобы использовать диаграмму Grafana на своем сайте. Это то, что мне тоже нужно без использования плейлиста ... Возможно ли?

Если вы просто хотите автоматически регистрировать пользователя, лучше всего использовать http://docs.grafana.org/tutorials/authproxy/.

@DanCech, проблема в том, что все пользователи могут автоматически входить в систему. Нам нужен способ автоматически входить в систему только тех пользователей, у которых есть определенный URL.

@gzzo , это зависит от дизайна прокси

Я тоже ищу эту функцию в Grafana.
Я думаю, что встраивание Microsoft Power BI - хорошее решение. См. Ссылки ниже.
https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embedding-Basics
https://microsoft.github.io/PowerBI-JavaScript/demo/v2-demo/index.html

Я думаю, что вышеприведенное решение PowerBi основано на OAuth2.
Сторона сервера Grafana должна поддерживать OAuth2 и включать CORS.

@Nayar Я новичок в Grafana. Что вы имеете в виду в виде акций Grafana и Nginx, которые вы прокомментировали 14 сентября 2017 года? У вас есть связь как?

С нетерпением жду релиза 5.4.

Удалось сделать это с успехом, используя auth.proxy Grafana и ngx_http_secure_link_module Nginx.

Ссылки, которые я использую, имеют формат http://grafana/?user=nayar&md5=2tutcea9nfdsfdsfdsw&expires=1505386800

Как только пользователь нажимает на нее, устанавливается cookie сеанса, и пользователь может просматривать графану, как если бы он вошел в систему в обычном режиме.

Преимущества:

  • Ссылка истекает через некоторое время + безопасность
  • Ссылка генерирует хеш с идентификатором пользователя, отметкой времени и ключом доступа + безопасность
  • Не нужно вводить пароль + удобство

Моя конфигурация nginx выглядит так

server {
  listen 3001 default_server;
#     listen [::]:3001 default_server;

  server_name _;

        location / {
      set $user "";
      set $state "";

      if ($args ~ "^user=(.+)&md5") {
                    set $user $1;
                    set $state "${state}U";
                }

      secure_link $arg_md5,$arg_expires;
                secure_link_md5 "$secure_link_expires$uri$user grafanarocks";

                if ($secure_link = "") { 
                    set $state "${state}S1";
                }

                if ($secure_link = "0") { 
                    set $state "${state}S2";
                }

                add_header X-uristate "$state";

                if ($state = "US1") { return 403; }
                if ($state = "US2") { return 410; }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://127.0.0.1:3000;
            }
    }

Не стесняйтесь обращаться ко мне, если вам понадобится помощь с этим

Привет, Наяр,
Я пытаюсь сделать интеграцию графана с SaaS.
Требование заключается в том, что после того, как пользователь войдет в мое веб-приложение и затем щелкните ссылку «grafana» на моей странице, он будет перенаправлен на панель управления Grafana без ввода имени пользователя и пароля.

Ваш комментарий, кажется, соответствует моим требованиям. Но я все еще не совсем понимаю процесс аутентификации.
Когда proxy_pass to grafana, как проверить, что пользователь аутентифицирован моим собственным приложением.

Мое веб-приложение использует X-XSRF-TOKEN для проверки полномочий пользователя.

Я новичок в настройке сервера и благодарен за вашу помощь.

@torkelo / @bergquist Предупреждаем - вот как мы работали над этим в Screenly . Он жив и отлично работает! Спасибо за тяжелую работу, ребята!

@torkelo / @bergquist Предупреждаем - вот как мы работали над этим в Screenly . Он жив и отлично работает! Спасибо за тяжелую работу, ребята!

@vpetersson Я не использую screenly, но мне любопытно, как вы смогли это реализовать. Я понимаю создание токена API. Вы все еще использовали iframe в URL-адресе? Как вы добавили токен аутентификации в заголовки запроса?

@GimpMaster Мы просто используем заголовок auth, поэтому

Я только что заставил свой режим киоска работать, используя подход Screenly, связанный с @GimpMaster . Я использовал Chrome Extension ModHeader, где я добавил заголовок авторизации с помощью ключа API носителя. Но поскольку расширениям требуется некоторое время для загрузки сна, 10 секунд от Настройка киоска на Raspberry Pi сделали

@Nayar Я успешно адаптировал предложенную вами конфигурацию ngnix для использования небезопасного_ доступа. То есть он просто берет имя пользователя из строки запроса и помещает его в X-WEBAUTH-USER. Затем он получает идентификатор сеанса графаны, и мы уезжаем. Спасибо!

Я не могу заставить работать метод md5. Я не понимаю, что именно мне нужно, чтобы сделать отпечаток пальца md5. Не могли бы вы уточнить? Есть какие-нибудь хитрости по изготовлению мд5?

Возможно ли реализовать какие-либо решения для входа в систему по токену / cookie / header / ... в grafana 6. *?

Я не могу сделать это с помощью простой панели ссылок на пустом PHP

index.php

<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script language="javaScript">
$( document ).ready(function() {
$.ajax({
        type: "GET",
        //url: "http://page.test;/grafana/",
        url: "http://page.test;/grafana/d/o24Tt1Cik/dashboard-test?orgId=1",
        contentType: "application/json",
        xhrFields: {
            withCredentials: false
        },
        beforeSend: function(request) {
                     request.setRequestHeader("X-WEBAUTH-USER", "admin");
                },
        headers: {
            // Set any custom headers here.
        "X-WEBAUTH-USER" : "admin"
        },
        success: function(data){
                var iframeDoc = $("#monitoringframe").get(0).contentDocument;
                iframeDoc.open();
                iframeDoc.write(data);
                iframeDoc.close();
//$("#monitoringframe").attr('srcdoc',data)
        },
        error : function(err) {
            console.log('Error!', err)
        }
    });
});

</script>
<iframe name="monitoringframe" id="monitoringframe" src="about:blank" sandbox="allow-forms allow-popups allow-same-origin allow-scripts" width=100% height=600 border="0" frameborder="0" />
</body>
</html>

nginx

 server {
  listen 80;
  server_name page.test;
  root /var/www/page;
  index index.html index.htm index.php;

        location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
        location /grafana/ {

                proxy_pass http://localhost:3000/;

       }

}

grafana.ini

[auth.proxy]
# Defaults to false, but set to true to enable this feature
enabled = true
# HTTP Header name that will contain the username or email
header_name = X-WEBAUTH-USER
# HTTP Header property, defaults to `username` but can also be `email`
header_property = username
# Set to `true` to enable auto sign up of users who do not exist in Grafana DB. Defaults to `true`.
auto_sign_up = true
# If combined with Grafana LDAP integration define sync interval
ldap_sync_ttl = 60
# Limit where auth proxy requests come from by configuring a list of IP addresses.
# This can be used to prevent users spoofing the X-WEBAUTH-USER header.
# Example `whitelist = 192.168.1.1, 192.168.1.0/24, 2001::23, 2001::0/120`
whitelist = 127.0.0.1, ::1/120
# Optionally define more headers to sync other user attributes
# Example `headers = Name:X-WEBAUTH-NAME Email:X-WEBAUTH-EMAIL`
headers =

[auth.basic]
;enabled = true

Я протестировал изменение моей конфигурации nginx на это и установил "? User = myUser" в URL-адресе, но получил только несанкционированный ответ

location /grafana/ {

                error_log /var/www/grafana/error.log;
                access_log /var/www/grafana/access.log;

                set $user "";

                if ($args ~ "^user=(.+)") {
                    set $user $1;
                }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://localhost:3000/;

       }

recorte

@ mr0bles Все, что авторизации, объясняется в документации . Раньше не видел решения, в котором вы вызываете прокси с помощью "X-WEBAUTH-USER - обычно вы аутентифицируетесь в своем прокси, который, в свою очередь, заполняет и предоставляет X-WEBAUTH-USER графане.

@marefr спасибо за ответ.
Я прочитал документацию, и мой API работает, но мне нужна динамическая авторизация пользователя (не одно и то же разрешение для всех пользователей страницы)

Это решение @Nayar включает X-WEBAUTH-USER в заголовок без жесткого кода, но я не могу заставить его работать

Думаю что то сломалось в 6.0 или 6.1.
Вчера я обновил полностью рабочую установку с использованием прокси-сервера аутентификации в Apache2 до версии 6.1 (с версии 5.3) и получаю точно такие же экраны, что и @ mr0bles

Обратите внимание, что Grafana, похоже, понимает заголовок прокси: он сначала выполняет вход пользователя в систему, но последующие запросы получают ответ 401.

@Bitblade, мы не получали других отчетов, подобных

Удалось сделать это с успехом, используя auth.proxy Grafana и ngx_http_secure_link_module Nginx.

Ссылки, которые я использую, имеют формат http://grafana/?user=nayar&md5=2tutcea9nfdsfdsfdsw&expires=1505386800

Как только пользователь нажимает на нее, устанавливается cookie сеанса, и пользователь может просматривать графану, как если бы он вошел в систему в обычном режиме.

Преимущества:

  • Ссылка истекает через некоторое время + безопасность
  • Ссылка генерирует хеш с идентификатором пользователя, отметкой времени и ключом доступа + безопасность
  • Не нужно вводить пароль + удобство

Моя конфигурация nginx выглядит так

server {
  listen 3001 default_server;
#     listen [::]:3001 default_server;

  server_name _;

        location / {
      set $user "";
      set $state "";

      if ($args ~ "^user=(.+)&md5") {
                    set $user $1;
                    set $state "${state}U";
                }

      secure_link $arg_md5,$arg_expires;
                secure_link_md5 "$secure_link_expires$uri$user grafanarocks";

                if ($secure_link = "") { 
                    set $state "${state}S1";
                }

                if ($secure_link = "0") { 
                    set $state "${state}S2";
                }

                add_header X-uristate "$state";

                if ($state = "US1") { return 403; }
                if ($state = "US2") { return 410; }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://127.0.0.1:3000;
            }
    }

Не стесняйтесь обращаться ко мне, если вам понадобится помощь с этим

Это решение мне не подходит. Потому что я думаю, что каждый запрос должен содержать допустимый заголовок X-WEBAUTH-USER. Не получится, если при первом запросе заполнить заголовок, получить куки и пойти с ними.

Я пришел к следующему решению:

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  client_max_body_size 10m;
  root /foo/public;

  location /grafana/ {
    auth_request /gauth;
    auth_request_set $user $upstream_http_user;

    proxy_set_header X-WEBAUTH-USER $user;
    proxy_pass http://localhost:4000/;
  }

  location / {
    try_files $uri @app;
  }

  location <strong i="23">@app</strong> {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect  off;
  }
}

Таким образом, решение использует модуль nginx auth_request. Мое приложение отвечает за контроль доступа (запрос / gauth) и возвращает имя пользователя в заголовке ответа User .

Привет, а пока вы можете использовать мое решение, которое описано здесь: https://github.com/guysoft/FullPageOS/issues/175

Вот мой обходной путь с использованием Generic OAuth Authentication и PHP Grafana: https://github.com/nbayramberdiyev/grafana-generic-oauth

Надеюсь, что это поможет вам.

просто хотел прокомментировать, как я решил это, используя http basic auth https://github.com/grafana/grafana/issues/13706#issuecomment -540958284

Было бы неплохо иметь автоматический вход в систему, передающий токен пользователя через URL-адрес, это могло бы быть частичным решением для встраивания фреймов на сайты.

@rca вы можете привести пример со своим кодом? Я скопировал ваш код и embed.js, но он не сработал.

Если вы используете oauth, есть способ запустить встроенную графану без двойного входа

  1. Настройте grafana с одним поставщиком oauth и без других механизмов входа
  2. Установите для GF_AUTH_OAUTH_AUTO_LOGIN значение true
  3. Разместите графану в подпутье и используйте обратный прокси, чтобы графана обслуживалась на том же хосте, что и ваше основное приложение.
  4. Подключите основное приложение и графану к одному и тому же провайдеру oauth (для провайдера oauth они будут одним и тем же "приложением")
  5. Вставить графана
  6. Когда загружается iframe, grafana попытается автоматически войти в систему с помощью oauth (что должно быть успешным, поскольку оно находится в том же домене, что и ваше основное приложение, таким образом, используя тот же файл cookie аутентификации)

РЕДАКТИРОВАТЬ: вам может потребоваться установить security.cookie_samesite=none в grafana, чтобы это работало правильно в некоторых браузерах (это связано с тем, что в iframe происходит перенаправление поставщику oauth (другой домен), а затем перенаправление обратно на ваш grafana. В настоящее время firefox не позволяет сохранять таким образом cookie-файл того же сайта, для которого установлено значение lax . https://bugzilla.mozilla.org/show_bug.cgi?id=1454027, что означает, что grafana теряет свой oauth_state cookie, если cookie_samesite не установлен в none )

@seanlaff Я пробовал ваше решение с AWS Cognito, но оно возвращает заголовок, который не позволяет поместить его в iframe (X-Frame-Options: deny), есть какие-нибудь советы по этому поводу?

Удалось сделать это с успехом, используя auth.proxy Grafana и ngx_http_secure_link_module Nginx.
Ссылки, которые я использую, имеют формат http://grafana/?user=nayar&md5=2tutcea9nfdsfdsfdsw&expires=1505386800
Как только пользователь нажимает на нее, устанавливается cookie сеанса, и пользователь может просматривать графану, как если бы он вошел в систему в обычном режиме.
Преимущества:

  • Ссылка истекает через некоторое время + безопасность
  • Ссылка генерирует хеш с идентификатором пользователя, отметкой времени и ключом доступа + безопасность
  • Не нужно вводить пароль + удобство

Моя конфигурация nginx выглядит так

server {
    listen 3001 default_server;
#   listen [::]:3001 default_server;

    server_name _;

        location / {
        set $user "";
        set $state "";

        if ($args ~ "^user=(.+)&md5") {
                    set $user $1;
                    set $state "${state}U";
                }

        secure_link $arg_md5,$arg_expires;
                secure_link_md5 "$secure_link_expires$uri$user grafanarocks";

                if ($secure_link = "") { 
                    set $state "${state}S1";
                }

                if ($secure_link = "0") { 
                    set $state "${state}S2";
                }

                add_header X-uristate "$state";

                if ($state = "US1") { return 403; }
                if ($state = "US2") { return 410; }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://127.0.0.1:3000;
            }
    }

Не стесняйтесь обращаться ко мне, если вам понадобится помощь с этим

Это решение мне не подходит. Потому что я думаю, что каждый запрос должен содержать допустимый заголовок X-WEBAUTH-USER. Не получится, если при первом запросе заполнить заголовок, получить куки и пойти с ними.

Я пришел к следующему решению:

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  client_max_body_size 10m;
  root /foo/public;

  location /grafana/ {
    auth_request /gauth;
    auth_request_set $user $upstream_http_user;

    proxy_set_header X-WEBAUTH-USER $user;
    proxy_pass http://localhost:4000/;
  }

  location / {
    try_files $uri @app;
  }

  location <strong i="24">@app</strong> {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect  off;
  }
}

Таким образом, решение использует модуль nginx auth_request. Мое приложение отвечает за контроль доступа (запрос / gauth) и возвращает имя пользователя в заголовке ответа User .

как использовать с iframe?
я новичок в Grafana и NGINX
поэтому, пожалуйста, поделитесь максимальной информацией сейчас, чтобы изменить

как использовать с iframe?

@pgsekaran это решение не для iframe, а для получения пользовательского интерфейса Grafana без явного входа в систему Grafana для пользователей вашего приложения. Приложение в данном случае является прокси-сервером, который знает, как войти в систему Grafana.

Здравствуй,
Спасибо за быстрый ответ. есть ли у вас ссылка для добавления Grafana с другим приложением, использующим iframe. я добавляю Grafana в мое приложение, но я не могу установить управление использованием.
С уважением, Гуна
Во вторник, 23 июня 2020 г., 21:54:57 GMT + 5: 30 Константин Колотюк [email protected] написал:

как использовать с iframe?

@pgsekaran это решение не для iframe, а для получения пользовательского интерфейса Grafana без явного входа в систему Grafana для пользователей вашего приложения. Приложение в данном случае является прокси-сервером, который знает, как войти в систему Grafana.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub или откажитесь от подписки.

Все еще не реализовано? Серьезно, это хорошая функция.

@pgsekaran У меня есть решение для iframe, которое не очень безопасно, потому что оно использует логин на основе имени пользователя и полагается на то, что имена пользователей трудно угадать. По сути, имя пользователя - это токен.
Я написал об этом здесь, не очень подробно, но для начала
https://blog.yadamiel.com/tutorials/embed-and-authenticate-grafana-in-a-iframe

@pgsekaran У меня есть решение для iframe, которое не очень безопасно, потому что оно использует логин на основе имени пользователя и полагается на то, что имена пользователей трудно угадать. По сути, имя пользователя - это токен.
Я написал об этом здесь, не очень подробно, но для начала
https://blog.yadamiel.com/tutorials/embed-and-authenticate-grafana-in-a-iframe

Здравствуй,

Мне нужно настроить NGINX и iframe с автоматическим входом

Точная проблема была объяснена в https://github.com/grafana/grafana/issues/16319#issuecomment -483272921: идентификатор сеанса не возвращается с первым ответом, содержащим "скелет" страницы. Последующие запросы не содержат токен автоматического входа в систему, поэтому они терпят неудачу.

Мы используем хром в режиме киоска для отображения информационных панелей Grafana в разных местах. Поскольку тот же экземпляр Grafana также доступен для пользователей, мы используем auth.generic_oauth ( auth.basic до 5.x) для входа в систему людей и auth.proxy для входа в машины в режиме киоска:

/usr/bin/chromium --app="https://server.localdomain/grafana/d/000000004/002-the-big-picture?orgId=1&refresh=5m&autologin=lHOrdypkhxzNYb2lRaIjbNPlOCZw9gWE"

Как отмечали другие, это не просто работает. Что будет работать, так это сначала вызвать URL-адрес http://prometheus.localdomain/grafana/login?autologin=lHOrdypkhxzNYb2lRaIjbNPlOCZw9gWE (который теперь устанавливает grafana_session cookie), а затем перенаправить на реальную панель управления. Но ... режим киоска 🤷 и окна iframe 🤦‍♂️

Наше окончательное рабочее решение - объединить параметр запроса autologin с настраиваемым файлом cookie. Да, вы не можете выйти из системы через графический интерфейс, поскольку пользовательский файл cookie не удаляется, но поскольку этот механизм используется только на компьютерах в режиме киоска, в этом не было необходимости.

Итак, вот конфигурация nginx для сервера Grafana:

# this maps tokens to grafana users
map $arg_autologin $autologin {
    lHOrdypkhxzNYb2lRaIjbNPlOCZw9gWE "display-1";
    default "";
}

server {
    listen 80;

    server_name server.localdomain;

    # add_header cannot be used in an "if"-context
    # so we set it to an empty string here as 
    # `add_header Set-Cookie "";` just removes the complete
    # header from the response
    set $setCookieHeader "";

    # when the autologin query param is not set, use
    # the value from the cookie named `grafana_autologin`
    if ($arg_autologin = "") {
        set $arg_autologin $cookie_grafana_autologin;
    }

    # when either the autologin query param or the `grafana_autologin`
    # cookie was set, place the autologin token and the cookie path
    # in the variable. `path=/` is needed to allow deeplinking
    if ($arg_autologin != "") {
        set $setCookieHeader "grafana_autologin=$arg_autologin;path=/";
    }

    location /grafana/ {
        rewrite  ^/grafana/(.*)  /$1 break;

        # now send the Set-Cookie header when an autologin token was provided
        add_header Set-Cookie $setCookieHeader;

        # look up the autologin token in the map above and set the grafana user
        proxy_set_header X-WEBAUTH-USER $autologin;
        proxy_pass http://localhost:3000;
    }
}
Была ли эта страница полезной?
1 / 5 - 1 рейтинги

Смежные вопросы

SATHVIKRAJU picture SATHVIKRAJU  ·  3Комментарии

deepujain picture deepujain  ·  3Комментарии

KlavsKlavsen picture KlavsKlavsen  ·  3Комментарии

jackmeagher picture jackmeagher  ·  3Комментарии

victorhooi picture victorhooi  ·  3Комментарии