mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-01-16 14:52:43 -08:00
Translated ['.github/pull_request_template.md', 'src/pentesting-cloud/az
This commit is contained in:
@@ -4,91 +4,90 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
**The original author of this page is** [**Jorge**](https://www.linkedin.com/in/jorge-belmonte-a924b616b/) **(read his original post** [**here**](https://sickrov.github.io)**)**
|
||||
**Оригінальний автор цієї сторінки** [**Хорхе**](https://www.linkedin.com/in/jorge-belmonte-a924b616b/) **(читайте його оригінальну публікацію** [**тут**](https://sickrov.github.io)**)**
|
||||
|
||||
## Architecture & Basics
|
||||
## Архітектура та основи
|
||||
|
||||
### What does Kubernetes do?
|
||||
### Що робить Kubernetes?
|
||||
|
||||
- Allows running container/s in a container engine.
|
||||
- Schedule allows containers mission efficient.
|
||||
- Keep containers alive.
|
||||
- Allows container communications.
|
||||
- Allows deployment techniques.
|
||||
- Handle volumes of information.
|
||||
- Дозволяє запускати контейнери в контейнерному двигуні.
|
||||
- Планування дозволяє ефективно виконувати місії контейнерів.
|
||||
- Підтримує життєздатність контейнерів.
|
||||
- Дозволяє комунікацію між контейнерами.
|
||||
- Дозволяє техніки розгортання.
|
||||
- Обробляє обсяги інформації.
|
||||
|
||||
### Architecture
|
||||
### Архітектура
|
||||
|
||||

|
||||
|
||||
- **Node**: operating system with pod or pods.
|
||||
- **Pod**: Wrapper around a container or multiple containers with. A pod should only contain one application (so usually, a pod run just 1 container). The pod is the way kubernetes abstracts the container technology running.
|
||||
- **Service**: Each pod has 1 internal **IP address** from the internal range of the node. However, it can be also exposed via a service. The **service has also an IP address** and its goal is to maintain the communication between pods so if one dies the **new replacement** (with a different internal IP) **will be accessible** exposed in the **same IP of the service**. It can be configured as internal or external. The service also actuates as a **load balancer when 2 pods are connected** to the same service.\
|
||||
When a **service** is **created** you can find the endpoints of each service running `kubectl get endpoints`
|
||||
- **Kubelet**: Primary node agent. The component that establishes communication between node and kubectl, and only can run pods (through API server). The kubelet doesn’t manage containers that were not created by Kubernetes.
|
||||
- **Kube-proxy**: is the service in charge of the communications (services) between the apiserver and the node. The base is an IPtables for nodes. Most experienced users could install other kube-proxies from other vendors.
|
||||
- **Sidecar container**: Sidecar containers are the containers that should run along with the main container in the pod. This sidecar pattern extends and enhances the functionality of current containers without changing them. Nowadays, We know that we use container technology to wrap all the dependencies for the application to run anywhere. A container does only one thing and does that thing very well.
|
||||
- **Master process:**
|
||||
- **Api Server:** Is the way the users and the pods use to communicate with the master process. Only authenticated request should be allowed.
|
||||
- **Scheduler**: Scheduling refers to making sure that Pods are matched to Nodes so that Kubelet can run them. It has enough intelligence to decide which node has more available resources the assign the new pod to it. Note that the scheduler doesn't start new pods, it just communicate with the Kubelet process running inside the node, which will launch the new pod.
|
||||
- **Kube Controller manager**: It checks resources like replica sets or deployments to check if, for example, the correct number of pods or nodes are running. In case a pod is missing, it will communicate with the scheduler to start a new one. It controls replication, tokens, and account services to the API.
|
||||
- **etcd**: Data storage, persistent, consistent, and distributed. Is Kubernetes’s database and the key-value storage where it keeps the complete state of the clusters (each change is logged here). Components like the Scheduler or the Controller manager depends on this date to know which changes have occurred (available resourced of the nodes, number of pods running...)
|
||||
- **Cloud controller manager**: Is the specific controller for flow controls and applications, i.e: if you have clusters in AWS or OpenStack.
|
||||
- **Вузол**: операційна система з подом або подами.
|
||||
- **Под**: обгортка навколо контейнера або кількох контейнерів. Под повинен містити лише один додаток (тому зазвичай под запускає лише 1 контейнер). Под є способом, яким Kubernetes абстрагує технологію контейнерів.
|
||||
- **Сервіс**: Кожен под має 1 внутрішню **IP-адресу** з внутрішнього діапазону вузла. Однак його також можна відкрити через сервіс. **Сервіс також має IP-адресу** і його мета - підтримувати зв'язок між подами, тому якщо один з них зламається, **новий замінник** (з іншою внутрішньою IP) **буде доступний** через **ту ж IP-адресу сервісу**. Його можна налаштувати як внутрішній або зовнішній. Сервіс також діє як **балансувальник навантаження, коли 2 поди підключені** до одного сервісу.\
|
||||
Коли **сервіс** створено, ви можете знайти кінцеві точки кожного сервісу, запустивши `kubectl get endpoints`
|
||||
- **Kubelet**: Основний агент вузла. Компонент, який встановлює зв'язок між вузлом і kubectl, і може запускати лише поди (через API-сервер). Kubelet не керує контейнерами, які не були створені Kubernetes.
|
||||
- **Kube-proxy**: це сервіс, відповідальний за комунікацію (сервіси) між apiserver і вузлом. Основою є IPtables для вузлів. Найбільш досвідчені користувачі можуть встановлювати інші kube-proxies від інших постачальників.
|
||||
- **Контейнер Sidecar**: Контейнери Sidecar - це контейнери, які повинні працювати разом з основним контейнером у поді. Цей шаблон Sidecar розширює та покращує функціональність поточних контейнерів без їх зміни. Сьогодні ми знаємо, що використовуємо технологію контейнерів, щоб обернути всі залежності для запуску додатка в будь-якому місці. Контейнер виконує лише одну задачу і робить це дуже добре.
|
||||
- **Головний процес:**
|
||||
- **Api Server:** Це спосіб, яким користувачі та поди спілкуються з головним процесом. Дозволяються лише автентифіковані запити.
|
||||
- **Планувальник**: Планування стосується забезпечення того, щоб поди відповідали вузлам, щоб Kubelet міг їх запускати. Він має достатньо інтелекту, щоб вирішити, який вузол має більше доступних ресурсів, і призначити новий под. Зверніть увагу, що планувальник не запускає нові поди, він лише спілкується з процесом Kubelet, що працює всередині вузла, який запустить новий под.
|
||||
- **Kube Controller manager**: Він перевіряє ресурси, такі як набори реплік або розгортання, щоб перевірити, чи, наприклад, працює правильна кількість подів або вузлів. У разі відсутності пода він зв'яжеться з планувальником, щоб запустити новий. Він контролює реплікацію, токени та облікові сервіси для API.
|
||||
- **etcd**: Сховище даних, постійне, узгоджене та розподілене. Це база даних Kubernetes і сховище ключ-значення, де зберігається повний стан кластерів (кожна зміна тут реєструється). Компоненти, такі як планувальник або менеджер контролера, залежать від цих даних, щоб знати, які зміни відбулися (доступні ресурси вузлів, кількість запущених подів...)
|
||||
- **Cloud controller manager**: Це специфічний контролер для управління потоками та додатками, тобто: якщо у вас є кластери в AWS або OpenStack.
|
||||
|
||||
Note that as the might be several nodes (running several pods), there might also be several master processes which their access to the Api server load balanced and their etcd synchronized.
|
||||
Зверніть увагу, що оскільки може бути кілька вузлів (які запускають кілька подів), також може бути кілька головних процесів, доступ до яких до API-сервера збалансовано, а їх etcd синхронізовано.
|
||||
|
||||
**Volumes:**
|
||||
**Томи:**
|
||||
|
||||
When a pod creates data that shouldn't be lost when the pod disappear it should be stored in a physical volume. **Kubernetes allow to attach a volume to a pod to persist the data**. The volume can be in the local machine or in a **remote storage**. If you are running pods in different physical nodes you should use a remote storage so all the pods can access it.
|
||||
Коли под створює дані, які не повинні бути втрачені, коли под зникає, їх слід зберігати в фізичному томі. **Kubernetes дозволяє прикріпити том до пода для збереження даних**. Том може бути на локальному комп'ютері або в **віддаленому сховищі**. Якщо ви запускаєте поди на різних фізичних вузлах, вам слід використовувати віддалене сховище, щоб усі поди могли до нього отримати доступ.
|
||||
|
||||
**Other configurations:**
|
||||
**Інші конфігурації:**
|
||||
|
||||
- **ConfigMap**: You can configure **URLs** to access services. The pod will obtain data from here to know how to communicate with the rest of the services (pods). Note that this is not the recommended place to save credentials!
|
||||
- **Secret**: This is the place to **store secret data** like passwords, API keys... encoded in B64. The pod will be able to access this data to use the required credentials.
|
||||
- **Deployments**: This is where the components to be run by kubernetes are indicated. A user usually won't work directly with pods, pods are abstracted in **ReplicaSets** (number of same pods replicated), which are run via deployments. Note that deployments are for **stateless** applications. The minimum configuration for a deployment is the name and the image to run.
|
||||
- **StatefulSet**: This component is meant specifically for applications like **databases** which needs to **access the same storage**.
|
||||
- **Ingress**: This is the configuration that is use to **expose the application publicly with an URL**. Note that this can also be done using external services, but this is the correct way to expose the application.
|
||||
- If you implement an Ingress you will need to create **Ingress Controllers**. The Ingress Controller is a **pod** that will be the endpoint that will receive the requests and check and will load balance them to the services. the ingress controller will **send the request based on the ingress rules configured**. Note that the ingress rules can point to different paths or even subdomains to different internal kubernetes services.
|
||||
- A better security practice would be to use a cloud load balancer or a proxy server as entrypoint to don't have any part of the Kubernetes cluster exposed.
|
||||
- When request that doesn't match any ingress rule is received, the ingress controller will direct it to the "**Default backend**". You can `describe` the ingress controller to get the address of this parameter.
|
||||
- `minikube addons enable ingress`
|
||||
- **ConfigMap**: Ви можете налаштувати **URL-адреси** для доступу до сервісів. Под отримає дані звідси, щоб знати, як спілкуватися з іншими сервісами (поди). Зверніть увагу, що це не рекомендоване місце для збереження облікових даних!
|
||||
- **Secret**: Це місце для **зберігання секретних даних**, таких як паролі, API-ключі... закодованих у B64. Под зможе отримати доступ до цих даних, щоб використовувати необхідні облікові дані.
|
||||
- **Deployments**: Це місце, де вказуються компоненти, які будуть запущені Kubernetes. Користувач зазвичай не працює безпосередньо з подами, поди абстраговані в **ReplicaSets** (кількість однакових подів, що реплікуються), які запускаються через розгортання. Зверніть увагу, що розгортання призначені для **безстанційних** додатків. Мінімальна конфігурація для розгортання - це ім'я та образ для запуску.
|
||||
- **StatefulSet**: Цей компонент призначений спеціально для додатків, таких як **бази даних**, які потребують **доступу до одного й того ж сховища**.
|
||||
- **Ingress**: Це конфігурація, яка використовується для **публічного відкриття додатка з URL-адресою**. Зверніть увагу, що це також можна зробити за допомогою зовнішніх сервісів, але це правильний спосіб відкрити додаток.
|
||||
- Якщо ви реалізуєте Ingress, вам потрібно буде створити **Ingress Controllers**. Ingress Controller - це **под**, який буде кінцевою точкою, що отримуватиме запити, перевірятиме їх і балансуватиме навантаження на сервіси. Ingress controller **надсилатиме запит на основі налаштованих правил ingress**. Зверніть увагу, що правила ingress можуть вказувати на різні шляхи або навіть піддомени для різних внутрішніх сервісів Kubernetes.
|
||||
- Кращою практикою безпеки буде використання хмарного балансувальника навантаження або проксі-сервера як точки входу, щоб жодна частина кластера Kubernetes не була відкрита.
|
||||
- Коли отримується запит, який не відповідає жодному правилу ingress, контролер ingress направить його на "**Default backend**". Ви можете `describe` контролер ingress, щоб отримати адресу цього параметра.
|
||||
- `minikube addons enable ingress`
|
||||
|
||||
### PKI infrastructure - Certificate Authority CA:
|
||||
### Інфраструктура PKI - Центр сертифікації CA:
|
||||
|
||||

|
||||
|
||||
- CA is the trusted root for all certificates inside the cluster.
|
||||
- Allows components to validate to each other.
|
||||
- All cluster certificates are signed by the CA.
|
||||
- ETCd has its own certificate.
|
||||
- types:
|
||||
- apiserver cert.
|
||||
- kubelet cert.
|
||||
- scheduler cert.
|
||||
- CA є надійним коренем для всіх сертифікатів у кластері.
|
||||
- Дозволяє компонентам перевіряти один одного.
|
||||
- Усі сертифікати кластера підписані CA.
|
||||
- ETCd має свій власний сертифікат.
|
||||
- типи:
|
||||
- сертифікат apiserver.
|
||||
- сертифікат kubelet.
|
||||
- сертифікат планувальника.
|
||||
|
||||
## Basic Actions
|
||||
## Основні дії
|
||||
|
||||
### Minikube
|
||||
|
||||
**Minikube** can be used to perform some **quick tests** on kubernetes without needing to deploy a whole kubernetes environment. It will run the **master and node processes in one machine**. Minikube will use virtualbox to run the node. See [**here how to install it**](https://minikube.sigs.k8s.io/docs/start/).
|
||||
|
||||
**Minikube** можна використовувати для виконання деяких **швидких тестів** на Kubernetes без необхідності розгортання цілого середовища Kubernetes. Він запустить **головні та вузлові процеси на одному комп'ютері**. Minikube використовуватиме virtualbox для запуску вузла. Дивіться [**тут, як його встановити**](https://minikube.sigs.k8s.io/docs/start/).
|
||||
```
|
||||
$ minikube start
|
||||
😄 minikube v1.19.0 on Ubuntu 20.04
|
||||
✨ Automatically selected the virtualbox driver. Other choices: none, ssh
|
||||
💿 Downloading VM boot image ...
|
||||
> minikube-v1.19.0.iso.sha256: 65 B / 65 B [-------------] 100.00% ? p/s 0s
|
||||
> minikube-v1.19.0.iso: 244.49 MiB / 244.49 MiB 100.00% 1.78 MiB p/s 2m17.
|
||||
> minikube-v1.19.0.iso.sha256: 65 B / 65 B [-------------] 100.00% ? p/s 0s
|
||||
> minikube-v1.19.0.iso: 244.49 MiB / 244.49 MiB 100.00% 1.78 MiB p/s 2m17.
|
||||
👍 Starting control plane node minikube in cluster minikube
|
||||
💾 Downloading Kubernetes v1.20.2 preload ...
|
||||
> preloaded-images-k8s-v10-v1...: 491.71 MiB / 491.71 MiB 100.00% 2.59 MiB
|
||||
> preloaded-images-k8s-v10-v1...: 491.71 MiB / 491.71 MiB 100.00% 2.59 MiB
|
||||
🔥 Creating virtualbox VM (CPUs=2, Memory=3900MB, Disk=20000MB) ...
|
||||
🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.4 ...
|
||||
▪ Generating certificates and keys ...
|
||||
▪ Booting up control plane ...
|
||||
▪ Configuring RBAC rules ...
|
||||
▪ Generating certificates and keys ...
|
||||
▪ Booting up control plane ...
|
||||
▪ Configuring RBAC rules ...
|
||||
🔎 Verifying Kubernetes components...
|
||||
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
|
||||
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
|
||||
🌟 Enabled addons: storage-provisioner, default-storageclass
|
||||
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by defaul
|
||||
|
||||
@@ -106,11 +105,9 @@ $ minikube delete
|
||||
🔥 Deleting "minikube" in virtualbox ...
|
||||
💀 Removed all traces of the "minikube" cluster
|
||||
```
|
||||
### Kubectl Основи
|
||||
|
||||
### Kubectl Basics
|
||||
|
||||
**`Kubectl`** is the command line tool for kubernetes clusters. It communicates with the Api server of the master process to perform actions in kubernetes or to ask for data.
|
||||
|
||||
**`Kubectl`** - це інструмент командного рядка для кластерів kubernetes. Він спілкується з Api сервером головного процесу для виконання дій у kubernetes або для запиту даних.
|
||||
```bash
|
||||
kubectl version #Get client and server version
|
||||
kubectl get pod
|
||||
@@ -141,188 +138,172 @@ kubectl delete deployment mongo-depl
|
||||
#Deploy from config file
|
||||
kubectl apply -f deployment.yml
|
||||
```
|
||||
|
||||
### Minikube Dashboard
|
||||
|
||||
The dashboard allows you to see easier what is minikube running, you can find the URL to access it in:
|
||||
|
||||
Дашборд дозволяє вам легше бачити, що виконує minikube, ви можете знайти URL для доступу до нього в:
|
||||
```
|
||||
minikube dashboard --url
|
||||
|
||||
|
||||
🔌 Enabling dashboard ...
|
||||
▪ Using image kubernetesui/dashboard:v2.3.1
|
||||
▪ Using image kubernetesui/metrics-scraper:v1.0.7
|
||||
▪ Using image kubernetesui/dashboard:v2.3.1
|
||||
▪ Using image kubernetesui/metrics-scraper:v1.0.7
|
||||
🤔 Verifying dashboard health ...
|
||||
🚀 Launching proxy ...
|
||||
🤔 Verifying proxy health ...
|
||||
http://127.0.0.1:50034/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/
|
||||
```
|
||||
### YAML конфігураційні файли приклади
|
||||
|
||||
### YAML configuration files examples
|
||||
Кожен конфігураційний файл має 3 частини: **метадані**, **специфікація** (що потрібно запустити), **статус** (бажаний стан).\
|
||||
Всередині специфікації файлу конфігурації розгортання ви можете знайти шаблон, визначений з новою структурою конфігурації, що визначає образ для запуску:
|
||||
|
||||
Each configuration file has 3 parts: **metadata**, **specification** (what need to be launch), **status** (desired state).\
|
||||
Inside the specification of the deployment configuration file you can find the template defined with a new configuration structure defining the image to run:
|
||||
|
||||
**Example of Deployment + Service declared in the same configuration file (from** [**here**](https://gitlab.com/nanuchi/youtube-tutorial-series/-/blob/master/demo-kubernetes-components/mongo.yaml)**)**
|
||||
|
||||
As a service usually is related to one deployment it's possible to declare both in the same configuration file (the service declared in this config is only accessible internally):
|
||||
**Приклад розгортання + служби, оголошених в одному конфігураційному файлі (з** [**тут**](https://gitlab.com/nanuchi/youtube-tutorial-series/-/blob/master/demo-kubernetes-components/mongo.yaml)**)**
|
||||
|
||||
Оскільки служба зазвичай пов'язана з одним розгортанням, можливо оголосити обидва в одному конфігураційному файлі (служба, оголошена в цій конфігурації, доступна лише внутрішньо):
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mongodb-deployment
|
||||
labels:
|
||||
app: mongodb
|
||||
name: mongodb-deployment
|
||||
labels:
|
||||
app: mongodb
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mongodb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mongodb
|
||||
spec:
|
||||
containers:
|
||||
- name: mongodb
|
||||
image: mongo
|
||||
ports:
|
||||
- containerPort: 27017
|
||||
env:
|
||||
- name: MONGO_INITDB_ROOT_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mongodb-secret
|
||||
key: mongo-root-username
|
||||
- name: MONGO_INITDB_ROOT_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mongodb-secret
|
||||
key: mongo-root-password
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mongodb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mongodb
|
||||
spec:
|
||||
containers:
|
||||
- name: mongodb
|
||||
image: mongo
|
||||
ports:
|
||||
- containerPort: 27017
|
||||
env:
|
||||
- name: MONGO_INITDB_ROOT_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mongodb-secret
|
||||
key: mongo-root-username
|
||||
- name: MONGO_INITDB_ROOT_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mongodb-secret
|
||||
key: mongo-root-password
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mongodb-service
|
||||
name: mongodb-service
|
||||
spec:
|
||||
selector:
|
||||
app: mongodb
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 27017
|
||||
targetPort: 27017
|
||||
selector:
|
||||
app: mongodb
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 27017
|
||||
targetPort: 27017
|
||||
```
|
||||
**Приклад конфігурації зовнішнього сервісу**
|
||||
|
||||
**Example of external service config**
|
||||
|
||||
This service will be accessible externally (check the `nodePort` and `type: LoadBlancer` attributes):
|
||||
|
||||
Цей сервіс буде доступний зовні (перевірте атрибути `nodePort` та `type: LoadBlancer`):
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mongo-express-service
|
||||
name: mongo-express-service
|
||||
spec:
|
||||
selector:
|
||||
app: mongo-express
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8081
|
||||
targetPort: 8081
|
||||
nodePort: 30000
|
||||
selector:
|
||||
app: mongo-express
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8081
|
||||
targetPort: 8081
|
||||
nodePort: 30000
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This is useful for testing but for production you should have only internal services and an Ingress to expose the application.
|
||||
> Це корисно для тестування, але для продуктивного середовища у вас повинні бути лише внутрішні сервіси та Ingress для відкриття програми.
|
||||
|
||||
**Example of Ingress config file**
|
||||
|
||||
This will expose the application in `http://dashboard.com`.
|
||||
**Приклад файлу конфігурації Ingress**
|
||||
|
||||
Це відкриє програму за адресою `http://dashboard.com`.
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: dashboard-ingress
|
||||
namespace: kubernetes-dashboard
|
||||
name: dashboard-ingress
|
||||
namespace: kubernetes-dashboard
|
||||
spec:
|
||||
rules:
|
||||
- host: dashboard.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: kubernetes-dashboard
|
||||
servicePort: 80
|
||||
rules:
|
||||
- host: dashboard.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: kubernetes-dashboard
|
||||
servicePort: 80
|
||||
```
|
||||
**Приклад конфігураційного файлу секретів**
|
||||
|
||||
**Example of secrets config file**
|
||||
|
||||
Note how the password are encoded in B64 (which isn't secure!)
|
||||
|
||||
Зверніть увагу, що паролі закодовані в B64 (що не є безпечним!)
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mongodb-secret
|
||||
name: mongodb-secret
|
||||
type: Opaque
|
||||
data:
|
||||
mongo-root-username: dXNlcm5hbWU=
|
||||
mongo-root-password: cGFzc3dvcmQ=
|
||||
mongo-root-username: dXNlcm5hbWU=
|
||||
mongo-root-password: cGFzc3dvcmQ=
|
||||
```
|
||||
**Приклад ConfigMap**
|
||||
|
||||
**Example of ConfigMap**
|
||||
|
||||
A **ConfigMap** is the configuration that is given to the pods so they know how to locate and access other services. In this case, each pod will know that the name `mongodb-service` is the address of a pod that they can communicate with (this pod will be executing a mongodb):
|
||||
|
||||
**ConfigMap** - це конфігурація, яка надається подам, щоб вони знали, як знаходити та отримувати доступ до інших сервісів. У цьому випадку кожен под буде знати, що ім'я `mongodb-service` є адресою пода, з яким вони можуть спілкуватися (цей под буде виконувати mongodb):
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: mongodb-configmap
|
||||
name: mongodb-configmap
|
||||
data:
|
||||
database_url: mongodb-service
|
||||
database_url: mongodb-service
|
||||
```
|
||||
|
||||
Then, inside a **deployment config** this address can be specified in the following way so it's loaded inside the env of the pod:
|
||||
|
||||
Тоді, всередині **deployment config** ця адреса може бути вказана наступним чином, щоб вона завантажувалася в середовище pod:
|
||||
```yaml
|
||||
[...]
|
||||
spec:
|
||||
[...]
|
||||
template:
|
||||
[...]
|
||||
spec:
|
||||
containers:
|
||||
- name: mongo-express
|
||||
image: mongo-express
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
env:
|
||||
- name: ME_CONFIG_MONGODB_SERVER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: mongodb-configmap
|
||||
key: database_url
|
||||
[...]
|
||||
template:
|
||||
[...]
|
||||
spec:
|
||||
containers:
|
||||
- name: mongo-express
|
||||
image: mongo-express
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
env:
|
||||
- name: ME_CONFIG_MONGODB_SERVER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: mongodb-configmap
|
||||
key: database_url
|
||||
[...]
|
||||
```
|
||||
**Приклад конфігурації обсягу**
|
||||
|
||||
**Example of volume config**
|
||||
Ви можете знайти різні приклади файлів конфігурації зберігання yaml за адресою [https://gitlab.com/nanuchi/youtube-tutorial-series/-/tree/master/kubernetes-volumes](https://gitlab.com/nanuchi/youtube-tutorial-series/-/tree/master/kubernetes-volumes).\
|
||||
**Зверніть увагу, що обсяги не знаходяться всередині просторів імен**
|
||||
|
||||
You can find different example of storage configuration yaml files in [https://gitlab.com/nanuchi/youtube-tutorial-series/-/tree/master/kubernetes-volumes](https://gitlab.com/nanuchi/youtube-tutorial-series/-/tree/master/kubernetes-volumes).\
|
||||
**Note that volumes aren't inside namespaces**
|
||||
### Простори імен
|
||||
|
||||
### Namespaces
|
||||
Kubernetes підтримує **декілька віртуальних кластерів**, які базуються на одному фізичному кластері. Ці віртуальні кластери називаються **просторами імен**. Вони призначені для використання в середовищах з багатьма користувачами, розподіленими по кількох командах або проектах. Для кластерів з кількома до десятків користувачів вам не потрібно створювати або думати про простори імен. Ви повинні почати використовувати простори імен, щоб мати кращий контроль і організацію кожної частини програми, розгорнутої в kubernetes.
|
||||
|
||||
Kubernetes supports **multiple virtual clusters** backed by the same physical cluster. These virtual clusters are called **namespaces**. These are intended for use in environments with many users spread across multiple teams, or projects. For clusters with a few to tens of users, you should not need to create or think about namespaces at all. You only should start using namespaces to have a better control and organization of each part of the application deployed in kubernetes.
|
||||
|
||||
Namespaces provide a scope for names. Names of resources need to be unique within a namespace, but not across namespaces. Namespaces cannot be nested inside one another and **each** Kubernetes **resource** can only be **in** **one** **namespace**.
|
||||
|
||||
There are 4 namespaces by default if you are using minikube:
|
||||
Простори імен забезпечують область для імен. Імена ресурсів повинні бути унікальними в межах простору імен, але не між просторами імен. Простори імен не можуть бути вкладеними один в одного, і **кожен** ресурс Kubernetes може бути **тільки в** **одному** **просторі імен**.
|
||||
|
||||
За замовчуванням є 4 простори імен, якщо ви використовуєте minikube:
|
||||
```
|
||||
kubectl get namespace
|
||||
NAME STATUS AGE
|
||||
@@ -331,116 +312,108 @@ kube-node-lease Active 1d
|
||||
kube-public Active 1d
|
||||
kube-system Active 1d
|
||||
```
|
||||
|
||||
- **kube-system**: It's not meant or the users use and you shouldn't touch it. It's for master and kubectl processes.
|
||||
- **kube-public**: Publicly accessible date. Contains a configmap which contains cluster information
|
||||
- **kube-node-lease**: Determines the availability of a node
|
||||
- **default**: The namespace the user will use to create resources
|
||||
|
||||
- **kube-system**: Це не призначено для використання користувачами, і вам не слід його чіпати. Це для процесів master і kubectl.
|
||||
- **kube-public**: Публічно доступні дані. Містить configmap, який містить інформацію про кластер.
|
||||
- **kube-node-lease**: Визначає доступність вузла.
|
||||
- **default**: Простір імен, який користувач буде використовувати для створення ресурсів.
|
||||
```bash
|
||||
#Create namespace
|
||||
kubectl create namespace my-namespace
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Note that most Kubernetes resources (e.g. pods, services, replication controllers, and others) are in some namespaces. However, other resources like namespace resources and low-level resources, such as nodes and persistenVolumes are not in a namespace. To see which Kubernetes resources are and aren’t in a namespace:
|
||||
> Зверніть увагу, що більшість ресурсів Kubernetes (наприклад, pods, services, replication controllers та інші) знаходяться в деяких просторах імен. Однак інші ресурси, такі як ресурси простору імен та низькорівневі ресурси, такі як nodes і persistenVolumes, не знаходяться в просторі імен. Щоб побачити, які ресурси Kubernetes є в просторі імен, а які ні:
|
||||
>
|
||||
> ```bash
|
||||
> kubectl api-resources --namespaced=true #In a namespace
|
||||
> kubectl api-resources --namespaced=false #Not in a namespace
|
||||
> kubectl api-resources --namespaced=true #В просторі імен
|
||||
> kubectl api-resources --namespaced=false #Не в просторі імен
|
||||
> ```
|
||||
|
||||
You can save the namespace for all subsequent kubectl commands in that context.
|
||||
|
||||
Ви можете зберегти простір імен для всіх наступних команд kubectl у цьому контексті.
|
||||
```bash
|
||||
kubectl config set-context --current --namespace=<insert-namespace-name-here>
|
||||
```
|
||||
|
||||
### Helm
|
||||
|
||||
Helm is the **package manager** for Kubernetes. It allows to package YAML files and distribute them in public and private repositories. These packages are called **Helm Charts**.
|
||||
|
||||
Helm є **менеджером пакетів** для Kubernetes. Він дозволяє упаковувати YAML файли та розповсюджувати їх у публічних і приватних репозиторіях. Ці пакети називаються **Helm Charts**.
|
||||
```
|
||||
helm search <keyword>
|
||||
```
|
||||
Helm також є шаблонним двигуном, який дозволяє генерувати конфігураційні файли з змінними:
|
||||
|
||||
Helm is also a template engine that allows to generate config files with variables:
|
||||
## Kubernetes секрети
|
||||
|
||||
## Kubernetes secrets
|
||||
**Секрет** - це об'єкт, який **містить чутливі дані**, такі як пароль, токен або ключ. Таку інформацію інакше можна було б помістити в специфікацію Pod або в образ. Користувачі можуть створювати Секрети, а система також створює Секрети. Ім'я об'єкта Секрету повинно бути дійсним **DNS піддоменом**. Читайте тут [офіційну документацію](https://kubernetes.io/docs/concepts/configuration/secret/).
|
||||
|
||||
A **Secret** is an object that **contains sensitive data** such as a password, a token or a key. Such information might otherwise be put in a Pod specification or in an image. Users can create Secrets and the system also creates Secrets. The name of a Secret object must be a valid **DNS subdomain name**. Read here [the official documentation](https://kubernetes.io/docs/concepts/configuration/secret/).
|
||||
Секрети можуть бути такими, як:
|
||||
|
||||
Secrets might be things like:
|
||||
- API, SSH ключі.
|
||||
- OAuth токени.
|
||||
- Облікові дані, паролі (в чистому вигляді або b64 + шифрування).
|
||||
- Інформація або коментарі.
|
||||
- Код підключення до бази даних, рядки… .
|
||||
|
||||
- API, SSH Keys.
|
||||
- OAuth tokens.
|
||||
- Credentials, Passwords (plain text or b64 + encryption).
|
||||
- Information or comments.
|
||||
- Database connection code, strings… .
|
||||
Існують різні типи секретів у Kubernetes
|
||||
|
||||
There are different types of secrets in Kubernetes
|
||||
|
||||
| Builtin Type | Usage |
|
||||
| ----------------------------------- | ----------------------------------------- |
|
||||
| **Opaque** | **arbitrary user-defined data (Default)** |
|
||||
| kubernetes.io/service-account-token | service account token |
|
||||
| kubernetes.io/dockercfg | serialized \~/.dockercfg file |
|
||||
| kubernetes.io/dockerconfigjson | serialized \~/.docker/config.json file |
|
||||
| kubernetes.io/basic-auth | credentials for basic authentication |
|
||||
| kubernetes.io/ssh-auth | credentials for SSH authentication |
|
||||
| kubernetes.io/tls | data for a TLS client or server |
|
||||
| bootstrap.kubernetes.io/token | bootstrap token data |
|
||||
| Вбудований тип | Використання |
|
||||
| ------------------------------------ | ------------------------------------------ |
|
||||
| **Opaque** | **произвольні дані, визначені користувачем (за замовчуванням)** |
|
||||
| kubernetes.io/service-account-token | токен облікового запису служби |
|
||||
| kubernetes.io/dockercfg | серіалізований файл \~/.dockercfg |
|
||||
| kubernetes.io/dockerconfigjson | серіалізований файл \~/.docker/config.json |
|
||||
| kubernetes.io/basic-auth | облікові дані для базової аутентифікації |
|
||||
| kubernetes.io/ssh-auth | облікові дані для SSH аутентифікації |
|
||||
| kubernetes.io/tls | дані для TLS клієнта або сервера |
|
||||
| bootstrap.kubernetes.io/token | дані токена для завантаження |
|
||||
|
||||
> [!NOTE]
|
||||
> **The Opaque type is the default one, the typical key-value pair defined by users.**
|
||||
> **Тип Opaque є типом за замовчуванням, типовою парою ключ-значення, визначеною користувачами.**
|
||||
|
||||
**How secrets works:**
|
||||
**Як працюють секрети:**
|
||||
|
||||

|
||||
|
||||
The following configuration file defines a **secret** called `mysecret` with 2 key-value pairs `username: YWRtaW4=` and `password: MWYyZDFlMmU2N2Rm`. It also defines a **pod** called `secretpod` that will have the `username` and `password` defined in `mysecret` exposed in the **environment variables** `SECRET_USERNAME` \_\_ and \_\_ `SECRET_PASSWOR`. It will also **mount** the `username` secret inside `mysecret` in the path `/etc/foo/my-group/my-username` with `0640` permissions.
|
||||
|
||||
Наступний конфігураційний файл визначає **секрет** під назвою `mysecret` з 2 парами ключ-значення `username: YWRtaW4=` та `password: MWYyZDFlMmU2N2Rm`. Він також визначає **pod** під назвою `secretpod`, який матиме `username` та `password`, визначені в `mysecret`, доступними в **змінних середовища** `SECRET_USERNAME` \_\_ та \_\_ `SECRET_PASSWOR`. Він також **монтує** секрет `username` всередині `mysecret` за шляхом `/etc/foo/my-group/my-username` з правами `0640`.
|
||||
```yaml:secretpod.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mysecret
|
||||
name: mysecret
|
||||
type: Opaque
|
||||
data:
|
||||
username: YWRtaW4=
|
||||
password: MWYyZDFlMmU2N2Rm
|
||||
username: YWRtaW4=
|
||||
password: MWYyZDFlMmU2N2Rm
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: secretpod
|
||||
name: secretpod
|
||||
spec:
|
||||
containers:
|
||||
- name: secretpod
|
||||
image: nginx
|
||||
env:
|
||||
- name: SECRET_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mysecret
|
||||
key: username
|
||||
- name: SECRET_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mysecret
|
||||
key: password
|
||||
volumeMounts:
|
||||
- name: foo
|
||||
mountPath: "/etc/foo"
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- name: foo
|
||||
secret:
|
||||
secretName: mysecret
|
||||
items:
|
||||
- key: username
|
||||
path: my-group/my-username
|
||||
mode: 0640
|
||||
containers:
|
||||
- name: secretpod
|
||||
image: nginx
|
||||
env:
|
||||
- name: SECRET_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mysecret
|
||||
key: username
|
||||
- name: SECRET_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mysecret
|
||||
key: password
|
||||
volumeMounts:
|
||||
- name: foo
|
||||
mountPath: "/etc/foo"
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- name: foo
|
||||
secret:
|
||||
secretName: mysecret
|
||||
items:
|
||||
- key: username
|
||||
path: my-group/my-username
|
||||
mode: 0640
|
||||
```
|
||||
|
||||
```bash
|
||||
@@ -449,114 +422,97 @@ kubectl get pods #Wait until the pod secretpod is running
|
||||
kubectl exec -it secretpod -- bash
|
||||
env | grep SECRET && cat /etc/foo/my-group/my-username && echo
|
||||
```
|
||||
|
||||
### Secrets in etcd <a href="#discover-secrets-in-etcd" id="discover-secrets-in-etcd"></a>
|
||||
|
||||
**etcd** is a consistent and highly-available **key-value store** used as Kubernetes backing store for all cluster data. Let’s access to the secrets stored in etcd:
|
||||
|
||||
**etcd** - це послідовне та високо доступне **сховище ключ-значення**, яке використовується як основне сховище Kubernetes для всіх даних кластера. Давайте отримати доступ до секретів, збережених в etcd:
|
||||
```bash
|
||||
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep etcd
|
||||
```
|
||||
|
||||
You will see certs, keys and url’s were are located in the FS. Once you get it, you would be able to connect to etcd.
|
||||
|
||||
Ви побачите, де сертифікати, ключі та URL-адреси розташовані у файловій системі. Як тільки ви їх отримаєте, ви зможете підключитися до etcd.
|
||||
```bash
|
||||
#ETCDCTL_API=3 etcdctl --cert <path to client.crt> --key <path to client.ket> --cacert <path to CA.cert> endpoint=[<ip:port>] health
|
||||
|
||||
ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/etcd/ca.cert endpoint=[127.0.0.1:1234] health
|
||||
```
|
||||
|
||||
Once you achieve establish communication you would be able to get the secrets:
|
||||
|
||||
Якщо ви зможете встановити зв'язок, ви зможете отримати секрети:
|
||||
```bash
|
||||
#ETCDCTL_API=3 etcdctl --cert <path to client.crt> --key <path to client.ket> --cacert <path to CA.cert> endpoint=[<ip:port>] get <path/to/secret>
|
||||
|
||||
ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/etcd/ca.cert endpoint=[127.0.0.1:1234] get /registry/secrets/default/secret_02
|
||||
```
|
||||
**Додавання шифрування до ETCD**
|
||||
|
||||
**Adding encryption to the ETCD**
|
||||
|
||||
By default all the secrets are **stored in plain** text inside etcd unless you apply an encryption layer. The following example is based on [https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/)
|
||||
|
||||
За замовчуванням всі секрети **зберігаються у відкритому** тексті всередині etcd, якщо ви не застосуєте шар шифрування. Наступний приклад базується на [https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/)
|
||||
```yaml:encryption.yaml
|
||||
apiVersion: apiserver.config.k8s.io/v1
|
||||
kind: EncryptionConfiguration
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- aescbc:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: cjjPMcWpTPKhAdieVtd+KhG4NN+N6e3NmBPMXJvbfrY= #Any random key
|
||||
- identity: {}
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- aescbc:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: cjjPMcWpTPKhAdieVtd+KhG4NN+N6e3NmBPMXJvbfrY= #Any random key
|
||||
- identity: {}
|
||||
```
|
||||
|
||||
After that, you need to set the `--encryption-provider-config` flag on the `kube-apiserver` to point to the location of the created config file. You can modify `/etc/kubernetes/manifest/kube-apiserver.yaml` and add the following lines:
|
||||
|
||||
Після цього вам потрібно встановити прапорець `--encryption-provider-config` на `kube-apiserver`, щоб вказати на місце розташування створеного конфігураційного файлу. Ви можете змінити `/etc/kubernetes/manifest/kube-apiserver.yaml` і додати наступні рядки:
|
||||
```yaml
|
||||
containers:
|
||||
- command:
|
||||
- kube-apiserver
|
||||
- --encriyption-provider-config=/etc/kubernetes/etcd/<configFile.yaml>
|
||||
- command:
|
||||
- kube-apiserver
|
||||
- --encriyption-provider-config=/etc/kubernetes/etcd/<configFile.yaml>
|
||||
```
|
||||
|
||||
Scroll down in the volumeMounts:
|
||||
|
||||
Прокрутіть униз у volumeMounts:
|
||||
```yaml
|
||||
- mountPath: /etc/kubernetes/etcd
|
||||
name: etcd
|
||||
readOnly: true
|
||||
name: etcd
|
||||
readOnly: true
|
||||
```
|
||||
|
||||
Scroll down in the volumeMounts to hostPath:
|
||||
|
||||
Прокрутіть униз у volumeMounts до hostPath:
|
||||
```yaml
|
||||
- hostPath:
|
||||
path: /etc/kubernetes/etcd
|
||||
type: DirectoryOrCreate
|
||||
name: etcd
|
||||
path: /etc/kubernetes/etcd
|
||||
type: DirectoryOrCreate
|
||||
name: etcd
|
||||
```
|
||||
**Перевірка, що дані зашифровані**
|
||||
|
||||
Дані зашифровані при запису в etcd. Після перезапуску вашого `kube-apiserver`, будь-який новостворений або оновлений секрет повинен бути зашифрований при зберіганні. Щоб перевірити, ви можете використовувати програму командного рядка `etcdctl`, щоб отримати вміст вашого секрету.
|
||||
|
||||
1. Створіть новий секрет під назвою `secret1` у просторі імен `default`:
|
||||
|
||||
```
|
||||
kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
|
||||
```
|
||||
|
||||
**Verifying that data is encrypted**
|
||||
2. Використовуючи командний рядок etcdctl, прочитайте цей секрет з etcd:
|
||||
|
||||
Data is encrypted when written to etcd. After restarting your `kube-apiserver`, any newly created or updated secret should be encrypted when stored. To check, you can use the `etcdctl` command line program to retrieve the contents of your secret.
|
||||
`ETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C`
|
||||
|
||||
1. Create a new secret called `secret1` in the `default` namespace:
|
||||
де `[...]` повинні бути додаткові аргументи для підключення до сервера etcd.
|
||||
|
||||
```
|
||||
kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
|
||||
```
|
||||
3. Перевірте, що збережений секрет має префікс `k8s:enc:aescbc:v1:`, що вказує на те, що провайдер `aescbc` зашифрував отримані дані.
|
||||
4. Перевірте, що секрет правильно розшифрований при отриманні через API:
|
||||
|
||||
2. Using the etcdctl commandline, read that secret out of etcd:
|
||||
```
|
||||
kubectl describe secret secret1 -n default
|
||||
```
|
||||
|
||||
`ETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C`
|
||||
|
||||
where `[...]` must be the additional arguments for connecting to the etcd server.
|
||||
|
||||
3. Verify the stored secret is prefixed with `k8s:enc:aescbc:v1:` which indicates the `aescbc` provider has encrypted the resulting data.
|
||||
4. Verify the secret is correctly decrypted when retrieved via the API:
|
||||
|
||||
```
|
||||
kubectl describe secret secret1 -n default
|
||||
```
|
||||
|
||||
should match `mykey: bXlkYXRh`, mydata is encoded, check [decoding a secret](https://kubernetes.io/docs/concepts/configuration/secret#decoding-a-secret) to completely decode the secret.
|
||||
|
||||
**Since secrets are encrypted on write, performing an update on a secret will encrypt that content:**
|
||||
повинен відповідати `mykey: bXlkYXRh`, mydata закодовано, перевірте [декодування секрету](https://kubernetes.io/docs/concepts/configuration/secret#decoding-a-secret), щоб повністю декодувати секрет.
|
||||
|
||||
**Оскільки секрети зашифровані при запису, виконання оновлення секрету зашифрує цей вміст:**
|
||||
```
|
||||
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
|
||||
```
|
||||
**Останні поради:**
|
||||
|
||||
**Final tips:**
|
||||
|
||||
- Try not to keep secrets in the FS, get them from other places.
|
||||
- Check out [https://www.vaultproject.io/](https://www.vaultproject.io) for add more protection to your secrets.
|
||||
- Намагайтеся не зберігати секрети у файловій системі, отримуйте їх з інших місць.
|
||||
- Перегляньте [https://www.vaultproject.io/](https://www.vaultproject.io) для додаткового захисту ваших секретів.
|
||||
- [https://kubernetes.io/docs/concepts/configuration/secret/#risks](https://kubernetes.io/docs/concepts/configuration/secret/#risks)
|
||||
- [https://docs.cyberark.com/Product-Doc/OnlineHelp/AAM-DAP/11.2/en/Content/Integrations/Kubernetes_deployApplicationsConjur-k8s-Secrets.htm](https://docs.cyberark.com/Product-Doc/OnlineHelp/AAM-DAP/11.2/en/Content/Integrations/Kubernetes_deployApplicationsConjur-k8s-Secrets.htm)
|
||||
|
||||
## References
|
||||
## Посилання
|
||||
|
||||
{{#ref}}
|
||||
https://sickrov.github.io/
|
||||
@@ -567,7 +523,3 @@ https://www.youtube.com/watch?v=X48VuDVv0do
|
||||
{{#endref}}
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user