From c6d1b51a8a625d9adf978a77d04e6667b95d6419 Mon Sep 17 00:00:00 2001 From: thest1tch Date: Mon, 12 Jun 2023 14:31:58 +0500 Subject: [PATCH] add new --- .drone.yml | 10 +- docs/blog/index.md | 1 + docs/blog/posts/traefik-fa.md | 382 ++++++++++++++++++++ docs/images/other/office/excel_vba_01.png | Bin 0 -> 36215 bytes docs/other/chrome.md | 19 + docs/other/office/excel_vba.md | 422 ++++++++++++++++++++++ docs/proxmox/ct-template.md | 7 + docs/ubuntu/mariadb.md | 0 mkdocs.yml | 8 +- requirements.txt | 3 +- 10 files changed, 849 insertions(+), 3 deletions(-) create mode 100644 docs/blog/index.md create mode 100644 docs/blog/posts/traefik-fa.md create mode 100644 docs/images/other/office/excel_vba_01.png create mode 100755 docs/other/chrome.md create mode 100644 docs/other/office/excel_vba.md create mode 100755 docs/proxmox/ct-template.md mode change 100644 => 100755 docs/ubuntu/mariadb.md diff --git a/.drone.yml b/.drone.yml index d97bc5e..92ecbc7 100755 --- a/.drone.yml +++ b/.drone.yml @@ -10,12 +10,17 @@ steps: volumes: - name: site path: /site + - name: site-st1tru + path: /site-st1tru commands: - pip install -U -r ./requirements.txt - mkdocs build - cp -r site/ /site + - cp -r site/ /site-st1tru - chown 1000:1000 /site + - chown 1000:1000 /site-st1tru - chmod -R 777 /site + - chmod -R 777 /site-st1tru when: event: push branch: main @@ -53,4 +58,7 @@ steps: volumes: - name: site host: - path: /opt/appdata/mkdocswiki \ No newline at end of file + path: /opt/appdata/mkdocswiki +- name: site-st1tru + host: + path: /opt/appdata/nginx/st1t.ru \ No newline at end of file diff --git a/docs/blog/index.md b/docs/blog/index.md new file mode 100644 index 0000000..1050001 --- /dev/null +++ b/docs/blog/index.md @@ -0,0 +1 @@ +asd \ No newline at end of file diff --git a/docs/blog/posts/traefik-fa.md b/docs/blog/posts/traefik-fa.md new file mode 100644 index 0000000..a3eed88 --- /dev/null +++ b/docs/blog/posts/traefik-fa.md @@ -0,0 +1,382 @@ +# Руководство по Traefik Forward Auth – Простая защита через Google SSO + +![](https://st1t.ru/wp-content/uploads/2022/11/traefik-forward-auth-google-flow-840x525.png) + +---------- + +Хотите защитить свой стек Docker чем-то более надежным, чем обычная HTTP-аутентификация? Google OAuth2 SSO с Traefik Forward Auth — ваш выбор. + +Я был очень удивлен, узнав, что образ Thomseddon [Traefik Forward Auth](https://github.com/thomseddon/traefik-forward-auth) может защитить мои службы в Docker. Этот образ обеспечивает вход и аутентификацию OAuth/SSO для обратного прокси-сервера Traefik с использованием Traefik. + +> Почему Traefik Google OAuth2 для служб Docker? +> +> Google OAuth2 позволяет вам использовать свою учетную запись Google для входа в свои службы. Использование **Google OAuth с Traefik** позволит вам добавлять учетные записи в белый список, внедрять двухфакторную аутентификацию Google, а также обеспечивать единый вход (SSO) для ваших служб. Это не только обеспечивает удобство, поскольку вам не нужно часто входить в систему, но и повышает безопасность. + +Добавление Traefik OAuth2 для ваших служб Docker будет простым шагом и важным дополнением к вашему стеку Docker Traefik. + + +## НАСТРОЙКА TRAEFIK FORWARD AUTH ПРИ ПОМОЩИ GOOGLE OAUTH2 + +Добавление базовой аутентификации, предоставляемой Traefik, — это самый простой способ защитить ваш Docker и другие службы. + +В нашем предыдущем руководстве мы рассмотрели, как использовать учетные данные для входа .htpasswd и промежуточное ПО **Traefik auth middleware** для добавления базовой аутентификации с использованием меток. + +Для одной службы это может быть полезно, но это быстро становится неудобным и утомительным, когда приходится входить в несколько служб и для каждого сеанса браузера. + +После внедрения **Traefik forward authentication** теперь нужно войти в систему только один раз. И внедрив _Traefik OAuth2 с Google_, можно добавить двухфакторную аутентификацию (2FA), что делает этот метод намного более безопасным и удобным, чем использование обычной аутентификации. + +Если вы предпочитаете частную self-hosted систему многофакторной аутентификации (а не полагаться на Google), обратите внимание на Authelia или Keycloak. Я использовал Authelia для ранее, а сейчас перешел на Keyсloak для управления несколькими пользователями. Traefik OAuth2 я использую для домашних проектов. + +> ### Что такое OAuth? +> +> **OAuth** — открытый протокол (схема) [авторизации](https://ru.wikipedia.org/wiki/%D0%90%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F "Авторизация"), обеспечивающий предоставление третьей стороне ограниченный доступ к защищённым ресурсам пользователя без передачи ей (третьей стороне) логина и пароля [– Wikipedia](https://ru.wikipedia.org/wiki/OAuth) + +Прежде чем настраивать Traefik Forward Auth с Google OAuth2, давайте посмотрим, как все это сочетается друг с другом. + +### Как работает Google OAuth2 с Traefik? + +Вход и аутентификация Google OAuth для Traefik действует как посредник для ваших служб, разрешая или запрещая доступ после проверки авторизованного файла cookie в вашем браузере. Подводя итог, процесс выглядит примерно так: + +1. Для нашего хоста сделан **запрос** (например, https://traefik.example.com) +2. Запрос направляется нашим провайдером DNS на наш IP-адрес WAN, где порты 80 и 443 перенаправляются в контейнер Traefik. +3. Traefik видит входящий запрос и распознает, что **Forward Auth** определен в **метках** для этого хоста, поэтому запрос перенаправляется в контейнер **Traefik Forward Auth**. +4. Затем контейнер проверяет, есть ли в браузере авторизованный файл cookie. Если файл cookie отсутствует, запрос отправляется на **сервер авторизации Google OAuth2**. +5. После успешного входа в Google запрос отправляется на **URI перенаправления**, указанный для **веб-приложения** (https://oauth.example.com/_oauth). +6. Затем авторизованный файл cookie сохраняется в браузере, и пользователь отправляется во внутреннюю службу. + +В следующий раз, когда этот браузер попытается получить доступ к службе, защищенной входом и аутентификацией на основе OAuth, файл cookie будет распознан, и пользователь будет перенаправлен на их службу без запроса на вход! + +![](https://st1t.ru/wp-content/uploads/2022/11/traefik-forward-auth-google-flow-1024x654.png) + +Этот процесс происходит очень быстро, и как только ваш браузер получит файл cookie, вы забудете, что даже включили Google OAuth с Traefik! + +**Примечание.** Образ Traefik Forward Auth использует [**OpenID** **Connect**](https://en.wikipedia.org/wiki/OpenID_Connect) (**OIDC**),который представляет собой уровень аутентификации поверх протокола [OAuth 2.0](https://en.wikipedia.org/wiki/OAuth#OAuth_2.0 "OAuth"). Образ Docker, представленный в этом руководстве, поддерживает Google, а также [других поставщиков OIDC](https://github.com/thomseddon/traefik-forward-auth/wiki/Provider-Setup). + +Позаботившись об основах, давайте перейдем к настройке прямой аутентификации Google OAuth Traefik для наших сервисов Docker. + +> ### Как настроить OAuth? +> +> Настройка Google OAuth для Docker с помощью Traefik включает в себя 3 этапа: +> +> 1. создание записей DNS, +> 2. настройку службы Google OAuth2 и +> 3. изменение файлов компоновки Docker и добавление меток Traefik для активации прямой аутентификации. + +Итак, во-первых, нам нужно настроить службу Traefik OAuth2. Давайте настроим все предварительные условия сейчас: + +### Шаг 1. Создайте записи DNS + +Начните с создания новой DNS-записи CNAME для нашей службы OAuth (Google перенаправит на этот адрес после аутентификации). В этом руководстве мы будем использовать доменное имя **example.com**. + +Установите запись DNS как **oauth.example.com**. На рисунках ниже показан скриншот из Cloudflare. В зависимости от вашего провайдера DNS, для вас все может выглядеть по-разному, но по сути иметь одно и то же содержание. + +![](https://st1t.ru/wp-content/uploads/2022/11/01-clouldflare-dns-for-google-oauth-1024x367.png) + +Обратите внимание, что для распространения и активации записей DNS может потребоваться несколько часов. + +В качестве альтернативы, если вы включили запись CNAME с подстановочными знаками, указывающую на ваш корневой домен, вы можете пропустить этот шаг. + +### Шаг 2. Настройте службу Google OAuth2 + +Создав записи DNS, давайте перейдем к настройке Google OAuth. + +#### Шаг 2.1. Создайте проект Google. + +Нам нужно создать **проект Google**, который будет содержать наше **веб-приложение**, **экран согласия** и **учетные данные**. + +Перейдите в [Google Cloud Developers Console](https://console.developers.google.com/) и убедитесь, что вы вошли в правильный аккаунт Google, который хотите использовать (обычно это будет ваш адрес электронной почты). + +**Примечание.** Выйдите из других активных аккаунтов Google, чтобы убедиться, что на каждом этапе используется правильный аккаунт. + +При появлении запроса вам нужно будет согласиться с **Условиями использования Google Cloud Platform**, чтобы использовать их API. + +Сервис OAuth от Google можно использовать бесплатно, поэтому пока мы можем **отказаться** от бесплатной пробной версии. Нажмите «**Select a project**» и «**New project**». + +![](https://st1t.ru/wp-content/uploads/2022/11/02-google-cloud-oauth-create-new-project-1024x498.png) + +Введите уникальное имя для идентификации проекта, например **“Traefik Authentication”**. Нажмите **Create**. + +![](https://st1t.ru/wp-content/uploads/2022/11/03-google-cloud-oauth-new-project-details.png) + +#### Шаг 2.2. Создайте учетные данные OAuth + +Теперь, когда наш проект создан, нам нужно создать идентификатор клиента и секрет клиента, чтобы пройти аутентификацию в Google. Выберите наш проект **Traefik Authentication** и в меню навигации выберите **APIs & Services > Credentials**. Нажмите **Create Credentials > OAuth client ID**. + +![](https://st1t.ru/wp-content/uploads/2022/11/04-google-cloud-oauth-create-oauth-client-id.png) + +#### Шаг 2.3: Настройте Consent Screen + +После того, как вы нажмете Идентификатор клиента OAuth, вы увидите примечание о настройке consent screen, как показано ниже. Перед продолжением необходимо настроить consent screen. + +После того, как вы нажмете **OAuth Client ID**, вы увидите примечание о настройке consent screen, как показано ниже. Перед продолжением необходимо настроить consent screen. + +![](https://st1t.ru/wp-content/uploads/2022/11/05-google-cloud-oauth-configure-consent-screen.png) + +Если автоматический запрос не появится, выберите **OAuth consent screen** на левой панели. + +Далее выберите тип пользователей приложения. Выбираем тип **External** и нажимаем **Create**. + +![](https://st1t.ru/wp-content/uploads/2022/11/06-google-cloud-oauth-consent-screen-type.png) + +Выберите имя для своего приложения, например **Traefik Auth**, затем в разделе **Authorized domains** введите свой домен, например **example.com**. Убедитесь, что вы нажимаете **Enter**, чтобы добавить его, а затем нажмите **Save**. + +![](https://st1t.ru/wp-content/uploads/2022/11/07-google-cloud-oauth-consent-screen.png) + +После нажатия кнопки **Save** вы перейдете на следующий шаг. Везде нажимаем снизу **Save and Continue**. + +#### **Шаг 2.4. Создайте идентификатор клиента OAuth.** + +Выберите наш проект **Traefik Authentication** и в меню навигации выберите **APIs & Services > Credentials**. Нажмите **Create Credentials > OAuth client ID**. + +Теперь выберите тип **Web Application** и введите имя для своего веб-приложения, например, **Traefik**. Кроме того, вам потребуется ввести **Authorized redirect URI** в виде _https://oauth.example.com/_oauth_. Убедитесь, что вы нажимаете **Enter**, чтобы добавить его, а затем нажмите **Save**. + +**Примечание.** Вам разрешено добавлять только **URI перенаправления**, которые ведут на ваши **авторизованные домены**. Вернитесь к экрану согласия OAuth, если вам нужно их отредактировать. + +![](https://st1t.ru/wp-content/uploads/2022/11/08-oauth-authorized-redirect-uris.png) + +Учетные данные для нашего единого входа для Docker созданы! Скопируйте и сохраните **client ID** и **client secret**; нам нужно будет использовать их на следующем шаге. + +![](https://st1t.ru/wp-content/uploads/2022/11/09-oauth-client-id-and-secret.png) + +### Шаг 3. Настройте прямую аутентификацию Traefik с помощью OAuth2 + +Теперь, когда учетные данные OAuth настроены, осталось настроить контейнер OAuth. Войдите на свой локальный компьютер или используйте SSH-клиен для удаленного входа. + +Обязательно остановите Traefik (при необходимости) и отредактируйте файл docker-compose, добавив метки Traefik и контейнер OAuth, как описано ниже. + +Предполагается, что вы уже прочитали и выполнили большую часть моего руководства по Traefik 2 и читаете этот пост только для того, чтобы внедрить Google OAuth. + +#### Шаг 3.1. Создайте промежуточное ПО Traefik Auth и цепочку + +Давайте теперь укажем промежуточное ПО для аутентификации Traefik для OAuth2. Создайте новый файл middlewares-oauth.yml и добавьте следующие строки: +```yaml +http: + middlewares: + middlewares-oauth: + forwardAuth: + address: "http://oauth:4181" # Make sure you have the OAuth service in docker-compose.yml + trustForwardHeader: true + authResponseHeaders: + - "X-Forwarded-User" +``` +Кроме того, давайте создадим новую цепочку промежуточного программного обеспечения для сервисов, которые будут использовать Google OAuth. Откройте файл **middleware-chains.yml**, созданный в моем руководстве по Traefik, и добавьте следующие строки ниже того, что уже есть: +```yaml +http: + middlewares: + chain-oauth: + chain: + middlewares: + - middlewares-rate-limit + - middlewares-https-redirectscheme + - middlewares-secure-headers + - middlewares-oauth + - middlewares-compress +``` +**Примечание:** Промежуточное ПО для ограничения скорости, перенаправления HTTPS, заголовков безопасности и сжатия должно быть определено в соответствии с моим руководством Traefik. В противном случае Traefik не запустится. + +Сохраните файлы и выйдите из редактирования. + +#### Шаг 3.2: Добавьте секреты прямой аутентификации Traefik + +Вместо того, чтобы создавать несколько секретов, мы создадим один секретный файл для прямой аутентификации Traefik, который будет содержать все учетные данные. + +Создайте файл с именем **traefik_forward_auth** в папке **secrets** ($DOCKERDIR\secrets, если ваша установка основана на моих руководствах) и добавьте в него следующее содержимое: +``` +providers.google.client-id=yourGOOGLEclientID +providers.google.client-secret=yourCLIENTsecret +secret=yourOAUTHsecret +whitelist=yourEMAILaddress1 +whitelist=yourEMAILaddress2 +``` +Настройте вышеуказанное, используя информацию ниже: + +- **yourGOOGLEclientID** и **yourCLIENTsecret**: полученные ранее в этом руководстве по Traefik Oauth2. +- **yourOAUTHsecret**: используется для подписи файла cookie и должен быть случайным. Создайте случайный секрет с помощью: +`openssl rand -hex 16` + + +Кроме того, вы можете использовать [онлайн-сервис, подобный этому](https://www.browserling.com/tools/random-string), для создания случайного секрета. + +![](https://st1t.ru/wp-content/uploads/2022/11/10-random-secret.png) + +- **yourEMAILAddress1**: идентификатор электронной почты Google, который будет использоваться для аутентификации. При желании вы можете добавить дополнительные адреса электронной почты, которым разрешено проходить аутентификацию (например, **yourEMAILAddress2**; удалите эту строку, если вы хотите разрешить только один идентификатор электронной почты). + +Сохранить и выйти. + +Обратите внимание, что папка **secrets** и файл **traefik_forward_auth** должны иметь разрешение **600** и принадлежать пользователю **root**. + +Далее давайте настроим контейнер OAuth Forwarder. + +#### Шаг 3.3: Добавляем OAuth Forwarder контейнер + +Откройте файл docker-compose, который был создан на основе нашего предыдущего руководства по Traefik 2, и добавьте в него следующее. + +# Google OAuth - Single Sign On using OAuth 2.0 +```yaml +version: '3.7' +services: + oauth: + container_name: oauth + image: thomseddon/traefik-forward-auth:latest + restart: unless-stopped + security_opt: + - no-new-privileges:true + environment: + - CONFIG=/config + - COOKIE_DOMAIN=$DOMAINNAME_CLOUD_SERVER + - INSECURE_COOKIE=false + - AUTH_HOST=oauth.$DOMAINNAME_CLOUD_SERVER + - URL_PATH=/_oauth + - LOG_LEVEL=info + - LOG_FORMAT=text + - LIFETIME=86400 # 1 day + - DEFAULT_ACTION=auth + - DEFAULT_PROVIDER=google + secrets: + - source: traefik_forward_auth + target: /config + labels: + - "traefik.enable=true" + ## HTTP Routers + - "traefik.http.routers.oauth-rtr.tls=true" + - "traefik.http.routers.oauth-rtr.entrypoints=https" + - "traefik.http.routers.oauth-rtr.rule=Host(`oauth.$DOMAINNAME_CLOUD_SERVER`)" + ## Middlewares + - "traefik.http.routers.oauth-rtr.middlewares=chain-oauth@file" + ## HTTP Services + - "traefik.http.routers.oauth-rtr.service=oauth-svc" + - "traefik.http.services.oauth-svc.loadbalancer.server.port=4181" +``` +**$DOMAINNAME_CLOUD_SERVER** = имя вашего домена в файле .env + +Обратите внимание, что мы используем цепочку промежуточного программного обеспечения аутентификации Traefik (**chain-oauth**) для аутентификации вместо базовой аутентификации. Используйте правильный образ для архитектуры вашей системы. + +Вы также можете изменить продолжительность (**LIFETIME**), в течение которой аутентификация действительна, с 1 дня, указанного в секундах, на другую продолжительность. Сначала установите **LOG_LEVEL** для **trace** или **debug**. Когда все заработает, вы можете изменить его на **warn**. + +При желании остальные переменные среды, определенные для oauth , можно настроить по своему вкусу (если вы знаете, что делаете). + +После этого используйте команду docker-compose up. Теперь вы должны быть перенаправлены на страницу входа в систему Google OAuth перед доступом к службе. + +#### Шаг 3.4. Добавление Google OAuth для служб Docker + +Возьмем пример панели инструментов Traefik 2. Если вы следовали моему руководству по Traefik, теперь у вас должна быть базовая аутентификация с использованием промежуточного программного обеспечения: + +## Middlewares + +- "traefik.http.routers.traefik-rtr.middlewares=middlewares-basic-auth@file" + +Или цепочка промежуточного программного обеспечения: + +- "traefik.http.routers.traefik-rtr.middlewares=chain-basic-auth@file" + +Мы уже определили цепочку промежуточного программного обеспечения для Traefik oauth (**chain-oauth**) выше. + +Перейти с базовой аутентификации на oauth теперь так же просто, как изменить **chain-basic-auth** на **chain-oauth** в файле **docker-compose** (показано ниже). + +- "traefik.http.routers.traefik-rtr.middlewares=chain-oauth@file" + +Итак, вот и все, вход и аутентификация на основе OAuth для стека обратного прокси-сервера Traefik. + +После этого используйте команду **docker-compose up**. + +#### Шаг 3.5. Добавление OAuth в другие (не Docker) службы + +Приложениями, не относящимися к Docker, могут быть любые службы, находящиеся в хост-системе или в удаленной системе. + +Поставщик файлов Traefik позволяет нам добавлять динамические маршрутизаторы, промежуточное ПО и службы. Раньше мы использовали наш каталог rules только для добавления промежуточного программного обеспечения, но мы можем легко добавить внешний хост, добавив новый файл в этот каталог. Traefik автоматически обнаружит и обновит свои конфигурации. + +Давайте для примера возьмем сервис ex1, работающий на другом хосте (192.168.1.100). + +Чтобы добавить к сервису возможность аутентификации Traefik OAuth2, вам нужно всего лишь создать промежуточное ПО маршрутизатора, как показано ниже. + +http: + +routers: + +ex1-rtr: + +rule: "Host(`ex1.{{env "DOMAINNAME_CLOUD_SERVER"}}`)" + +entryPoints: + +- https + +middlewares: + +- chain-oauth + +service: ex1-svc + +tls: + +certResolver: dns-cloudflare + +services: + +ex1-svc: + +loadBalancer: + +servers: + +- url: "http://192.168.1.100:80" # or whatever your external host's IP:port is + +Интерфейс ex1 будет доступен по адресу **ex1.example.com**, при условии, что для переменной среды **DOMAINNAME_CLOUD_SERVER** установлено значение **example.com** и она передается в контейнер Traefik. + +Поскольку каталог rules является динамическим, просто добавив этот файл в этот каталог, мы создали маршрут. Вы должны иметь возможность подключиться к ex1 через Google OAuth2 без перезапуска Traefik! + +## ОБХОД OAUTH / ВЫБОРОЧНАЯ АУТЕНТИФИКАЦИЯ + +Некоторые приложения не могут аутентифицировать себя и пройти через авторизацию Oauth. Для этого есть обход Oauth на основе правил [Auth Forwarder GitHub](https://github.com/thomseddon/traefik-forward-auth). + +Вы можете обойти аутентификацию на основе определенных ключей в заголовках, регулярных выражениях, хосте, пути, запросе и многом другом. + +**Примечание.** Используя неправильно указанное правило, вы можете легко отключить аутентификацию, когда не собираетесь этого делать. Поэтому я настоятельно рекомендую конкретные и узкие правила обхода аутентификации. + +### Определение правила обхода + +Как мы можем выяснить, какие конкретные правила использовать? + +Это может быть довольно сложно. Как указано в приведенной ниже ссылке, общий способ обхода аутентификации — использование пути. + +Но это может ослабить безопасность, и я рекомендую установить определенные правила. Например, если в заголовке запроса содержится ключ API, аутентификация будет пропущена. + +Проверьте журналы Docker для контейнера OAuth (основные команды Docker). Это выдаст почти все, что происходит в этом контейнере, что может быть ошеломляющим. Вы можете сократить это до интересующего вас приложения, введя команду grep ****. + +### Указание правила обхода OAuth + +Обратите внимание, что **uri** в логах может содержать ключ API (X-Api-Key:[apikey]). Таким образом, вы можете установить использование обхода на основе этого следующим образом. На примере файла middleware сервиса ex1: + +В строчку + +rule: "Host(`ex1.{{env "DOMAINNAME_CLOUD_SERVER"}}`)" + +Дописываем + +rule: "Host(`ex1.{{env "DOMAINNAME_CLOUD_SERVER"}}`) && Headers(`X-Api-Key`, `$API_KEY`)" + +Перезапустите контейнер OAuth и попробуйте сейчас получить доступ к ex1 через приложение и браузер. Браузер должен представить логин OAuth, а приложение должно направить вас напрямую к сервису. + +### Использование пользовательских заголовков + +Некоторые приложения позволяют добавлять собственные заголовки. Поэтому, когда приложение пытается связаться с вашим Radarr, Sonarr и т. д., может быть отправлен собственный заголовок. + +Затем этот настраиваемый заголовок можно отслеживать и, если он присутствует, можно использовать для запуска обхода. Это альтернатива поиску ключей API. Вы можете установить один стандартный заголовок для всех приложений и использовать для всех одно и то же правило обхода. Это также устраняет догадки и пробы и ошибки, описанные выше. + +## OAUTH И ЗАГОЛОВКИ БЕЗОПАСНОСТИ + +Одна вещь, которую следует отметить при использовании службы **Traefik OAuth2**, — это ваши заголовки безопасности. Если служба использует **OAuth** и вы пытаетесь проверить, применяются ли ваши заголовки безопасности, вы, вероятно, получите более низкую оценку. + +## FAQ + +### **Что такое traefik-forward-auth-provider?** + +Прямая аутентификация Traefik — это служба, которая интегрируется со сторонним поставщиком аутентификации (например, Google, Microsoft, Authelia и т. д.) для защиты ваших служб и веб-приложений. + +### **В чем разница между OIDC и OAuth?** + +OIDC или OpenID Connect (OIDC) — это протокол аутентификации. Это набор спецификаций, основанный на OAuth 2.0, который добавляет дополнительные функции. По сути, OIDC — это аутентичный протокол, а OAuth — набор спецификаций для доступа к ресурсам и их совместного использования. + +### Существуют ли другие (более частные) альтернативы Google OAuth? + +Да. [Authelia](https://github.com/authelia/authelia) и [Keycloak](https://github.com/keycloak/keycloak) – два, о которых я могу вспомнить. ИМХО, Authelia была намного проще в настройке, чем Keycloak. Существует также [Authentik](https://goauthentik.io/). \ No newline at end of file diff --git a/docs/images/other/office/excel_vba_01.png b/docs/images/other/office/excel_vba_01.png new file mode 100644 index 0000000000000000000000000000000000000000..8e868c442b4f72355da16b17e5b353922fcd076f GIT binary patch literal 36215 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

G-li~c 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 literal 0 HcmV?d00001 diff --git a/docs/other/chrome.md b/docs/other/chrome.md new file mode 100755 index 0000000..79bdb86 --- /dev/null +++ b/docs/other/chrome.md @@ -0,0 +1,19 @@ +Google Chrome + +## Блокировка медиакнопок + +Одним словом, то пока можно только отключить. Но не весь браузер, а только упомянутую функцию поддержки мультимедийных кнопок в нем. Делается это путем отключения соответствующего «*флага*» (экспериментальной функции) в настройках Chrome. Для этого: + +1. открываем браузер в адресную строку копируем `chrome:flags/#hardware-media-key-handling` и жмем Enter +2. далее для функции Hardware Media Key Handling вместо значения «`Default`» устанавливаем «`Disabled` +3. перезапускаем Chrome, чтобы активировать изменение. + + + +
report - click this + +cpu_adam ............... [NO] ....... [OKAY] +cpu_adagrad ............ [NO] ....... [OKAY] +fused_adam ............. [NO] ....... [OKAY] + +
\ No newline at end of file diff --git a/docs/other/office/excel_vba.md b/docs/other/office/excel_vba.md new file mode 100644 index 0000000..b724151 --- /dev/null +++ b/docs/other/office/excel_vba.md @@ -0,0 +1,422 @@ +## Защита страницы через VBA + +Каждый кто хоть раз писал макросы, сталкивался с тем, что требуется защитить лист и формулы на нем от шаловливых ручек пользователей. При том, нужно чтобы макросы на листе работали. Самое просто решение, это перед кодом макроса, добавить это: + +```vba +Worksheets("Лист1").Unprotect Password:="123" +'тут макрос делает действия +Worksheets("Лист1").Protect Password:="123" +``` + +Это будет работать, но у такого подхода есть и свои минусы. Во-первых, нужно эту конструкцию размещать в каждом макросе. Во-вторых - если будет ошибка, лист останется без защиты. + +### Есть другой способ + +Нажмите `Alt+F11`, чтобы попасть в редактор Visual Basic. Затем в левом верхнем углу в окне Project Explorer (если его не видно, то нажмите Ctrl+R) модуль ЭтаКнига (ThisWorkbook) и откройте двойным щелчком: + +![]( ../../images/other/office/excel_vba_01.png){ loading=lazy } + + +Вставьте этот код: + +```vba +Private Sub Workbook_Open() + 'включаем защиту первого листа для пользователя, но не макроса + Worksheets("Лист1").Protect Password:="123", UserInterfaceOnly:=True + + 'второй лист защищаем аналогично, но с возможностью пользоваться группировкой + Worksheets("Лист2").EnableOutlining = True + Worksheets("Лист2").Protect Password:="555", UserInterfaceOnly:=True +End Sub +``` + +Данный код будет автоматически запускаться при открытии файла и ставить защиту на заданные листы. Параметр `UserInterfaceOnly` указывает Excel, что защита не должна работать для действий макроса, а только на операции юзера. +Второй параметр `EnableOutlining` разрешает пользоваться группировкой. + +## Исправление примечаний VBA + +Бывает так, что ты вставляешь примечания и они уползают вниз при действиях со строками.  + +Чтобы такого не было, используй этот макрос: + +```vba +Sub align_comments() +Dim x As Comment +For Each x In ActiveSheet.Comments + x.Shape.Left = x.Parent.Offset(0, 1).Left + 10 + x.Shape.Top = x.Parent.Top +Next +End Sub +``` + +## Открытие файла VBA + +Для открытия 1 файла и передачи его на дальнейшую обработку, используй этот код: + +```vba +avFiles = Application.GetOpenFilename _ + ("Excel files(*.xls*),*.xls*", 1, "Выбери Excel файл", , False) +If VarType(avFiles) = vbBoolean Then + 'была нажата кнопка отмены - выход из процедуры + Exit Sub +End If + +Set avFiles1 = Workbooks.Open(Filename:=avFiles) +``` + +Чтобы открыть много файлов и запустить обработку по ним, используем цикл: + +```vba +FilesToOpen = Application.GetOpenFilename _ + (FileFilter:="All files (*.*), *.*", _ + MultiSelect:=True, Title:="Files to Merge") + +If TypeName(FilesToOpen) = "Boolean" Then + MsgBox "Не выбрано ни одного файла!" + Exit Sub +End If + +'проходим по всем выбранным файлам +x = 1 +While x <= UBound(FilesToOpen) + With Workbooks.Open(FilesToOpen(x)).Sheets(1) + ... + ... + End With + x = x + 1 +Wend +``` + +## Отправка почты VBA + +отправляет почту, используя для этого запущенный MS Outlook + +```vba +' Запрос ввода темы письма + Dim vRetVal + vRetVal = InputBox("Введи тему письма", "Тема", "Test") + ActiveSheet.Range("I3").Value = vRetVal + + Dim OutApp As Object + Dim OutMail As Object + Dim cell As Range + + Send_ist = Cells(1, 1) + + Application.ScreenUpdating = False + Set OutApp = CreateObject("Outlook.Application") + OutApp.Session.Logon + Set OutMail = OutApp.CreateItem(0) + On Error Resume Next + With OutMail + .To = Send_ist + .Body = "Заполнить в день получения и отправить обратно на your_email" + .Subject = Range("A1").Value + .Attachments.Add iFullName + .Send + End With + + On Error GoTo 0 + Set OutMail = Nothing +``` + +## Отключение обновления экрана +```vba +Application.ScreenUpdating = False +' в конце +Application.ScreenUpdating = True +``` + +## Прогресс бар VBA + +> Использование APPLICATION.STATUSBAR +{.is-info} + +### Прогресс бар внизу страницы: +```vba +Application.StatusBar = "TEST" + DoEvents + ... +Application.StatusBar = False +``` + +### Прогресс бар для циклов: +```vba +For i = 1 To n + Istochnik = Cells(i, 20) + Application.StatusBar = "Делаю источник " & i & "-" & Istochnik & "/" & 50 & "" + DoEvents +Next +``` +### Можно сделать показ % завершения: + +![](https://pics.thest1tch.ru/pic/xwbza0bv.png) + +```vba +Sub ShowProgressBar() + Dim lAllCnt As Long, lr as Long + Dim rc As Range + 'кол-во ячеек в выделенной области + lAllCnt = Selection.Count + 'цикл по всем ячейкам в выделенной области + For Each rc In Selection + 'прибавляем 1 при каждом шаге + lr = lr + 1 + Application.StatusBar = "Выполнено: " & Int(100 * lr / lAllCnt) & "%" + DoEvents 'чтобы форма перерисовывалась + Next + 'сбрасываем значение статусной строки + Application.StatusBar = False +End Sub +``` +### Текст + блоки символов из 10 штук: + +![](https://pics.thest1tch.ru/pic/umeq366g.png) + +```vba +Sub StatusBar2() + Dim lr As Long, lp As Double + Dim lAllCnt As Long 'кол-во итераций + Dim s As String + lAllCnt = 10000 + For lr = 1 To lAllCnt + lp = lr \ 100 'десятая часть всего массива + 'формируем строку символов(от 1 до 10) + s = String(lp \ 10, ChrW(10152)) & String(11 - lp \ 10, ChrW(8700)) + Application.StatusBar = "Выполнено: " & lp & "% " & s: DoEvents + DoEvents + Next + 'очищаем статус-бар от значений после выполнения + Application.StatusBar = False +End Sub +``` + +### Текст + блоки квадратов из n штук: + +количество квадратов можно менять +![](https://pics.thest1tch.ru/pic/pq1o9ua4.png) + +```vba +Sub StatusBar3() + Dim lr As Long + Dim lAllCnt As Long 'кол-во итераций + Const lMaxQuad As Long = 20 'сколько квадратов выводить + lAllCnt = 10000 + + For lr = 1 To lAllCnt + Application.StatusBar = "Выполнено: " & Int(100 * lr / lAllCnt) & "%" & String(CLng(lMaxQuad * lr / lAllCnt), ChrW(9632)) & String(lMaxQuad - CLng(lMaxQuad * lr / lAllCnt), ChrW(9633)) + DoEvents + Next + 'очищаем статус-бар от значений после выполнения + Application.StatusBar = False +End Sub +``` + +## Создание файла VBA + +Простое + +```vba +iFullName = ThisWorkbook.Path & "name.xlsx" +``` +С именем из ячейки: + +```vba +iFullName = ThisWorkbook.Path & "\" & Range("A1").Value & ".xlsx" +``` + +## Список уникальных значений VBA + +```vba +PS = Range("A" & Rows.Count).End(xlUp).Row +Range("N6:N" & PS).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("T11"), Unique:=True +Range("T11:T300").Font.ColorIndex = 5 +MsgBox "Создали уникальный список источников" +``` + +## Удаление пароля VBA + +> Пожалуйста, сделайте резервную копию ваших файлов в первую очередь! +{.is-warning} + +### Для 32 битной версии +Откройте файл(ы), которые содержат ваши заблокированные проекты VBA +Создайте новый файл **xlsm** и сохраните этот код в `Module1` + +```vba +code credited to Siwtom (nick name), a Vietnamese developer + +Option Explicit + +Private Const PAGE_EXECUTE_READWRITE = &H40 + +Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" _ + (Destination As Long, Source As Long, ByVal Length As Long) + +Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As Long, _ + ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long + +Private Declare Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As Long + +Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, _ + ByVal lpProcName As String) As Long + +Private Declare Function DialogBoxParam Lib "user32" Alias "DialogBoxParamA" (ByVal hInstance As Long, _ + ByVal pTemplateName As Long, ByVal hWndParent As Long, _ + ByVal lpDialogFunc As Long, ByVal dwInitParam As Long) As Integer + +Dim HookBytes(0 To 5) As Byte +Dim OriginBytes(0 To 5) As Byte +Dim pFunc As Long +Dim Flag As Boolean + +Private Function GetPtr(ByVal Value As Long) As Long + GetPtr = Value +End Function + +Public Sub RecoverBytes() + If Flag Then MoveMemory ByVal pFunc, ByVal VarPtr(OriginBytes(0)), 6 +End Sub + +Public Function Hook() As Boolean + Dim TmpBytes(0 To 5) As Byte + Dim p As Long + Dim OriginProtect As Long + + Hook = False + + pFunc = GetProcAddress(GetModuleHandleA("user32.dll"), "DialogBoxParamA") + + + If VirtualProtect(ByVal pFunc, 6, PAGE_EXECUTE_READWRITE, OriginProtect) <> 0 Then + + MoveMemory ByVal VarPtr(TmpBytes(0)), ByVal pFunc, 6 + If TmpBytes(0) <> &H68 Then + + MoveMemory ByVal VarPtr(OriginBytes(0)), ByVal pFunc, 6 + + p = GetPtr(AddressOf MyDialogBoxParam) + + HookBytes(0) = &H68 + MoveMemory ByVal VarPtr(HookBytes(1)), ByVal VarPtr(p), 4 + HookBytes(5) = &HC3 + + MoveMemory ByVal pFunc, ByVal VarPtr(HookBytes(0)), 6 + Flag = True + Hook = True + End If + End If +End Function + +Private Function MyDialogBoxParam(ByVal hInstance As Long, _ + ByVal pTemplateName As Long, ByVal hWndParent As Long, _ + ByVal lpDialogFunc As Long, ByVal dwInitParam As Long) As Integer + If pTemplateName = 4070 Then + MyDialogBoxParam = 1 + Else + RecoverBytes + MyDialogBoxParam = DialogBoxParam(hInstance, pTemplateName, _ + hWndParent, lpDialogFunc, dwInitParam) + Hook + End If +End Function +``` +Вставьте этот код под вышеуказанным кодом в Module1 и запустите его +```vba +Sub unprotected() + If Hook Then + MsgBox "VBA Project is unprotected!", vbInformation, "*****" + End If +End Sub +``` +Возвращайтесь к своим проектам VBA и наслаждайтесь. + +### Для 64 битной версии: +Откройте файл (ы), содержащий ваши заблокированные проекты VBA. + +Создайте новый файл xlsm и сохраните этот код в `Module1` +```vba +Option Explicit + +Private Const PAGE_EXECUTE_READWRITE = &H40 + +Private Declare PtrSafe Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" _ +(Destination As LongPtr, Source As LongPtr, ByVal Length As LongPtr) + +Private Declare PtrSafe Function VirtualProtect Lib "kernel32" (lpAddress As LongPtr, _ +ByVal dwSize As LongPtr, ByVal flNewProtect As LongPtr, lpflOldProtect As LongPtr) As LongPtr + +Private Declare PtrSafe Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As LongPtr + +Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, _ +ByVal lpProcName As String) As LongPtr + +Private Declare PtrSafe Function DialogBoxParam Lib "user32" Alias "DialogBoxParamA" (ByVal hInstance As LongPtr, _ +ByVal pTemplateName As LongPtr, ByVal hWndParent As LongPtr, _ +ByVal lpDialogFunc As LongPtr, ByVal dwInitParam As LongPtr) As Integer + +Dim HookBytes(0 To 5) As Byte +Dim OriginBytes(0 To 5) As Byte +Dim pFunc As LongPtr +Dim Flag As Boolean + +Private Function GetPtr(ByVal Value As LongPtr) As LongPtr + GetPtr = Value +End Function + +Public Sub RecoverBytes() + If Flag Then MoveMemory ByVal pFunc, ByVal VarPtr(OriginBytes(0)), 6 +End Sub + +Public Function Hook() As Boolean + Dim TmpBytes(0 To 5) As Byte + Dim p As LongPtr + Dim OriginProtect As LongPtr + + Hook = False + + pFunc = GetProcAddress(GetModuleHandleA("user32.dll"), "DialogBoxParamA") + + + If VirtualProtect(ByVal pFunc, 6, PAGE_EXECUTE_READWRITE, OriginProtect) <> 0 Then + + MoveMemory ByVal VarPtr(TmpBytes(0)), ByVal pFunc, 6 + If TmpBytes(0) <> &H68 Then + + MoveMemory ByVal VarPtr(OriginBytes(0)), ByVal pFunc, 6 + + p = GetPtr(AddressOf MyDialogBoxParam) + + HookBytes(0) = &H68 + MoveMemory ByVal VarPtr(HookBytes(1)), ByVal VarPtr(p), 4 + HookBytes(5) = &HC3 + + MoveMemory ByVal pFunc, ByVal VarPtr(HookBytes(0)), 6 + Flag = True + Hook = True + End If + End If +End Function + +Private Function MyDialogBoxParam(ByVal hInstance As LongPtr, _ +ByVal pTemplateName As LongPtr, ByVal hWndParent As LongPtr, _ +ByVal lpDialogFunc As LongPtr, ByVal dwInitParam As LongPtr) As Integer + + If pTemplateName = 4070 Then + MyDialogBoxParam = 1 + Else + RecoverBytes + MyDialogBoxParam = DialogBoxParam(hInstance, pTemplateName, _ + hWndParent, lpDialogFunc, dwInitParam) + Hook + End If +End Function +``` +Вставьте этот код в Module2 и запустите его +```vba +Sub unprotected() + If Hook Then + MsgBox "VBA Project is unprotected!", vbInformation, "*****" + End If +End Sub +``` \ No newline at end of file diff --git a/docs/proxmox/ct-template.md b/docs/proxmox/ct-template.md new file mode 100755 index 0000000..4943b01 --- /dev/null +++ b/docs/proxmox/ct-template.md @@ -0,0 +1,7 @@ +Чтобы создать собственный шаблон контейнера в Proxmox, используйте: + +1. Сделайте резервную копию контейнера в формате gzip. В идеале, когда контейнер остановлен +2. SSH к базовой машине proxmox и переместите резервную копию контейнера в папку шаблона, используя: +3. Теперь можно использовать этот шаблон для создания новых контейнеров. + +`mv /var/lib/vz/dump/<имя-резервной копии>.tar.gz /var/lib/vz/template/cache/<имя-шаблона>.tar.gz` \ No newline at end of file diff --git a/docs/ubuntu/mariadb.md b/docs/ubuntu/mariadb.md old mode 100644 new mode 100755 diff --git a/mkdocs.yml b/mkdocs.yml index 245b74c..611f4f7 100755 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,11 +16,15 @@ nav: - Plex Meta Manager: docker-compose/plex-mm.md - Traefik: - Traefik Forward Auth: docker-compose/traefik-forward-auth.md - - :Ubuntu: Ubuntu 22: + - Ubuntu 22: - MariaDB: ubuntu/mariadb.md + - Proxmox: + - CT Template: proxmox/ct-template.md - Помойка: - SSL для сайта: other/ssl-for-site.md - Ubuntu bash aliases: other/ubuntu-bash-aliases.md + - Блог: + - blog/index.md theme: name: material @@ -85,6 +89,8 @@ plugins: lang: - ru - en + - glightbox + - blog markdown_extensions: - abbr diff --git a/requirements.txt b/requirements.txt index b831bb6..c383e08 100755 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ mkdocs-material mkdocs-material-extensions>=1.1 mkdocs-minify-plugin>=0.2 mkdocs-git-revision-date-plugin==0.3.1 -pymdown-extensions>=9.9.1 \ No newline at end of file +pymdown-extensions>=9.9.1 +mkdocs-glightbox \ No newline at end of file