เชนเซเช เชคเชฎเชพเชฐเชพ เชงเซเชฏเชพเชจ เชชเชฐ Dex, dex-k8s-authenticator เช เชจเซ GitHub เชจเซ เชเชชเชฏเซเช เชเชฐเซเชจเซ Kubernetes เชเซเชฒเชธเซเชเชฐเชจเซ เชเชเซเชธเซเชธ เชเชจเชฐเซเช เชเชฐเชตเชพ เชฎเชพเชเซเชจเซเช เชเซเชฏเซเชเซเชฐเซเชฏเชฒ เชฐเชเซ เชเชฐเซเช เชเซเช.
เชฐเชถเชฟเชฏเชจ-เชญเชพเชทเชพเชจเชพ เชเซเชฌเชฐเชจเซเชเซเชธ เชเซเช เชเชจเชจเชพ เชธเซเชฅเชพเชจเชฟเช เชฎเซเชฎ
เชชเชฐเชฟเชเชฏ
เช เชฎเซ เชตเชฟเชเชพเชธ เช เชจเซ QA เชเซเชฎ เชฎเชพเชเซ เชเชคเชฟเชถเซเชฒ เชตเชพเชคเชพเชตเชฐเชฃ เชฌเชจเชพเชตเชตเชพ เชฎเชพเชเซ Kubernetes เชจเซ เชเชชเชฏเซเช เชเชฐเซเช เชเซเช. เชคเซเชฅเซ เช เชฎเซ เชคเซเชฎเชจเซ เชกเซเชถเชฌเซเชฐเซเชก เช เชจเซ kubectl เชฌเชเชจเซ เชฎเชพเชเซ เชเซเชฒเชธเซเชเชฐเชจเซ เชเชเซเชธเซเชธ เชเชชเชตเชพ เชฎเชพเชเชเซเช เชเซเช. OpenShiftเชฅเซ เชตเชฟเชชเชฐเซเชค, เชตเซเชจเซเชฒเชพ เชเซเชฌเชฐเชจเซเชเซเชธ เชชเชพเชธเซ เชฎเซเชณ เชชเซเชฐเชฎเชพเชฃเซเชเชฐเชฃ เชจเชฅเซ, เชคเซเชฅเซ เช เชฎเซ เช เชฎเชพเชเซ เชคเซเชคเซเชฏ-เชชเชเซเชท เชธเชพเชงเชจเซเชจเซ เชเชชเชฏเซเช เชเชฐเซเช เชเซเช.
เช เชฐเซเชชเชฐเซเชเชพเชเชเชจเชฎเชพเช เช เชฎเซ เชเชชเชฏเซเช เชเชฐเซเช เชเซเช:
dex-k8s-เชเชฅเซเชจเซเชเชฟเชเซเชเชฐโ โ kubectl เชฐเซเชชเชฐเซเชเชพ เชฌเชจเชพเชตเชตเชพ เชฎเชพเชเซ เชตเซเชฌ เชเชชเซเชฒเชฟเชเซเชถเชจเชกเซเชเซเชธ - OpenID เชเชจเซเชเซเช เชชเซเชฐเชฆเชพเชคเชพ- GitHub - เชซเชเซเชค เชเชพเชฐเชฃ เชเซ เช เชฎเซ เช เชฎเชพเชฐเซ เชเชเชชเชจเซเชฎเชพเช GitHub เชจเซ เชเชชเชฏเซเช เชเชฐเซเช เชเซเช
เช
เชฎเซ Google OIDC เชจเซ เชเชชเชฏเซเช เชเชฐเชตเชพเชจเซ เชชเซเชฐเชฏเชพเชธ เชเชฐเซเชฏเซ, เชชเชฐเชเชคเซ เชเชฎเชจเชธเซเชฌเซ เช
เชฎเซ
เชคเซเชฅเซ, เช เชฎเชพเชฐเซ เชเซเชฌเชฐเชจเซเชเซเชธ เช เชงเชฟเชเซเชคเชคเชพ เชชเซเชฐเชเซเชฐเชฟเชฏเชพ เชฆเซเชฐเชถเซเชฏ เชฐเชเซเชเชคเชฎเชพเช เชเซเชตเซ เชฐเซเชคเซ เชเชพเชฐเซเชฏ เชเชฐเซ เชเซ:
เช
เชงเชฟเชเซเชคเชคเชพ เชชเซเชฐเชเซเชฐเชฟเชฏเชพ
เชฅเซเชกเซ เชตเชงเซ เชตเชฟเชเชค เช เชจเซ เชชเซเชเชจเซเช เชฌเชพเชฏ เชชเซเชเชจเซเช:
- เชตเชชเชฐเชพเชถเชเชฐเซเชคเชพ dex-k8s-เชเชฅเซเชจเซเชเชฟเชเซเชเชฐเชฎเชพเช เชฒเซเช เชเชจ เชเชฐเซ เชเซ (
login.k8s.example.com
) - dex-k8s-เชเชฅเซเชจเซเชเชฟเชเซเชเชฐ เชตเชฟเชจเชเชคเซเชจเซ เชกเซเชเซเชธเชจเซ เชซเซเชฐเชตเชฐเซเชก เชเชฐเซ เชเซ (
dex.k8s.example.com
) - เชกเซเชเซเชธ GitHub เชฒเซเชเชฟเชจ เชชเซเชทเซเช เชชเชฐ เชฐเซเชกเชพเชฏเชฐเซเชเซเช เชเชฐเซ เชเซ
- GitHub เชเชฐเซเชฐเซ เช เชงเชฟเชเซเชคเชคเชพ เชฎเชพเชนเชฟเชคเซ เชเชจเชฐเซเช เชเชฐเซ เชเซ เช เชจเซ เชคเซเชจเซ Dex เชชเชฐ เชชเชฐเชค เชเชฐเซ เชเซ
- Dex เชชเซเชฐเชพเชชเซเชค เชฎเชพเชนเชฟเชคเซเชจเซ dex-k8s-authenticator เชจเซ เชชเชธเชพเชฐ เชเชฐเซ เชเซ
- เชตเชชเชฐเชพเชถเชเชฐเซเชคเชพเชจเซ GitHub เชคเชฐเชซเชฅเซ OIDC เชเซเชเชจ เชฎเชณเซ เชเซ
- dex-k8s-authenticator kubeconfig เชฎเชพเช เชเซเชเชจ เชเชฎเซเชฐเซ เชเซ
- kubectl เชเซเชเชจ KubeAPIServer เชจเซ เชชเชธเชพเชฐ เชเชฐเซ เชเซ
- KubeAPIServer เชชเชพเชธ เชเชฐเซเชฒเชพ เชเซเชเชจเชจเชพ เชเชงเชพเชฐเซ kubectl เชชเชฐ เชเชเซเชธเซเชธ เชชเชฐเชค เชเชฐเซ เชเซ
- เชตเชชเชฐเชพเชถเชเชฐเซเชคเชพเชจเซ kubectl เชฎเชพเชเชฅเซ เชเชเซเชธเซเชธ เชฎเชณเซ เชเซ
เชชเซเชฐเชพเชฐเชเชญเชฟเช เชเซเชฐเชฟเชฏเชพเช
เช
เชฒเชฌเชคเซเชค, เช
เชฎเชพเชฐเซ เชชเชพเชธเซ เชชเชนเซเชฒเซเชฅเซ เช เชเซเชฌเชฐเชจเซเชเซเชธ เชเซเชฒเชธเซเชเชฐ เชเชจเซเชธเซเชเซเชฒ เชเชฐเซเชฒ เชเซ (k8s.example.com
), เช
เชจเซ เชชเซเชฐเซเชต-เชเชจเซเชธเซเชเซเชฒ เชเชฐเซเชฒ HELM เชธเชพเชฅเซ เชชเชฃ เชเชตเซ เชเซ. GitHub (super-org) เชชเชฐ เช
เชฎเชพเชฐเซ เชเช เชธเชเชธเซเชฅเชพ เชชเชฃ เชเซ.
เชเซ เชคเชฎเชพเชฐเซ เชชเชพเชธเซ HELM เชจเชฅเซ, เชคเซ เชคเซเชจเซ เชเชจเซเชธเซเชเซเชฒ เชเชฐเซ
เชชเซเชฐเชฅเชฎ เชเชชเชฃเซ GitHub เชธเซเช เชเชฐเชตเชพเชจเซ เชเชฐเซเชฐ เชเซ.
เชธเชเชธเซเชฅเชพ เชธเซเชเชฟเชเชเซเชธ เชชเซเชทเซเช เชชเชฐ เชเชพเช, (https://github.com/organizations/super-org/settings/applications
) เช
เชจเซ เชจเชตเซ เชเชชเซเชฒเชฟเชเซเชถเชจ เชฌเชจเชพเชตเซ (เช
เชงเชฟเชเซเชค OAuth เชเชชเซเชฒเชฟเชเซเชถเชจ):
GitHub เชชเชฐ เชจเชตเซ เชเชชเซเชฒเชฟเชเซเชถเชจ เชฌเชจเชพเชตเซ เชฐเชนเซ เชเซ
เชเชฐเซเชฐเซ URL เชธเชพเชฅเซ เชซเซเชฒเซเชก เชญเชฐเซ, เชเชฆเชพเชนเชฐเชฃ เชคเชฐเซเชเซ:
- เชนเซเชฎเชชเซเช URL:
https://dex.k8s.example.com
- เช
เชงเชฟเชเซเชคเชคเชพ เชเซเชฒเชฌเซเช URL:
https://dex.k8s.example.com/callback
เชฒเชฟเชเชเซเชธ เชธเชพเชฅเซ เชธเชพเชตเชเซเชค เชฐเชนเซ, เชธเซเชฒเซเชถ เชจ เชเซเชฎเชพเชตเชตเซเช เชฎเชนเชคเซเชตเชชเซเชฐเซเชฃ เชเซ.
เชชเซเชฐเซเชฃ เชเชฐเซเชฒ เชซเซเชฐเซเชฎเชจเชพ เชเชตเชพเชฌเชฎเชพเช, GitHub เชเชจเชฐเซเช เชเชฐเชถเซ Client ID
ะธ Client secret
, เชคเซเชฎเชจเซ เชธเซเชฐเชเซเชทเชฟเชค เชเชเซเชฏเชพเช เชฐเชพเชเซ, เชคเซเช เชเชชเชฃเชพ เชฎเชพเชเซ เชเชชเชฏเซเชเซ เชฅเชถเซ (เชเชฆเชพเชนเชฐเชฃ เชคเชฐเซเชเซ, เช
เชฎเซ เชเชชเชฏเซเช เชเชฐเซเช เชเซเช
Client ID: 1ab2c3d4e5f6g7h8
Client secret: 98z76y54x32w1
เชธเชฌเชกเซเชฎเซเชจเซเชธ เชฎเชพเชเซ DNS เชฐเซเชเซเชฐเซเชกเซเชธ เชคเซเชฏเชพเชฐ เชเชฐเซ login.k8s.example.com
ะธ dex.k8s.example.com
, เชคเซเชฎเช เชชเซเชฐเชตเซเชถ เชฎเชพเชเซ SSL เชชเซเชฐเชฎเชพเชฃเชชเชคเซเชฐเซ.
เชเชพเชฒเซ SSL เชชเซเชฐเชฎเชพเชฃเชชเชคเซเชฐเซ เชฌเชจเชพเชตเซเช:
cat <<EOF | kubectl create -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: cert-auth-dex
namespace: kube-system
spec:
secretName: cert-auth-dex
dnsNames:
- dex.k8s.example.com
acme:
config:
- http01:
ingressClass: nginx
domains:
- dex.k8s.example.com
issuerRef:
name: le-clusterissuer
kind: ClusterIssuer
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: cert-auth-login
namespace: kube-system
spec:
secretName: cert-auth-login
dnsNames:
- login.k8s.example.com
acme:
config:
- http01:
ingressClass: nginx
domains:
- login.k8s.example.com
issuerRef:
name: le-clusterissuer
kind: ClusterIssuer
EOF
kubectl describe certificates cert-auth-dex -n kube-system
kubectl describe certificates cert-auth-login -n kube-system
เชถเซเชฐเซเชทเช เชธเชพเชฅเซ เชเซเชฒเชธเซเชเชฐ เชเชถเซเชฏเซเช
เชฐ le-clusterissuer
เชชเชนเซเชฒเซเชฅเซ เช เช
เชธเซเชคเชฟเชคเซเชตเชฎเชพเช เชนเซเชตเซเช เชเซเชเช, เชชเชฐเชเชคเซ เชเซ เชจ เชนเซเชฏ, เชคเซ เชคเซเชจเซ HELM เชจเซ เชเชชเชฏเซเช เชเชฐเซเชจเซ เชฌเชจเชพเชตเซ:
helm install --namespace kube-system -n cert-manager stable/cert-manager
cat << EOF | kubectl create -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: le-clusterissuer
namespace: kube-system
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: le-clusterissuer
http01: {}
EOF
KubeAPIServer เชฐเซเชชเชฐเซเชเชพเชเชเชจ
kubeAPIServer เชเชพเชฎ เชเชฐเซ เชคเซ เชฎเชพเชเซ, เชคเชฎเชพเชฐเซ OIDC เชจเซ เชฐเซเชชเชฐเซเชเชพเชเชเชฟเชค เชเชฐเชตเชพเชจเซ เช เชจเซ เชเซเชฒเชธเซเชเชฐเชจเซ เช เชชเชกเซเช เชเชฐเชตเชพเชจเซ เชเชฐเซเชฐ เชเซ:
kops edit cluster
...
kubeAPIServer:
anonymousAuth: false
authorizationMode: RBAC
oidcClientID: dex-k8s-authenticator
oidcGroupsClaim: groups
oidcIssuerURL: https://dex.k8s.example.com/
oidcUsernameClaim: email
kops update cluster --yes
kops rolling-update cluster --yes
เช
เชฎเซ เชเชชเชฏเซเช เชเชฐเซเช เชเซเช
เชกเซเชเซเชธ เชฐเซเชชเชฐเซเชเชพเชเชเชจ เช เชจเซ dex-k8s-เชเชฅเซเชจเซเชเชฟเชเซเชเชฐ
เชกเซเชเซเชธ เชเชพเชฎ เชเชฐเชตเชพ เชฎเชพเชเซ, เชคเชฎเชพเชฐเซ เชชเชพเชธเซ เชเซเชฌเชฐเชจเซเชเซเชธ เชฎเชพเชธเซเชเชฐเชจเซเช เชชเซเชฐเชฎเชพเชฃเชชเชคเซเชฐ เช เชจเซ เชเซ เชนเซเชตเซ เชเชฐเซเชฐเซ เชเซ, เชเชพเชฒเซ เชคเซเชจเซ เชคเซเชฏเชพเชเชฅเซ เชฎเซเชณเชตเซเช:
sudo cat /srv/kubernetes/ca.{crt,key}
-----BEGIN CERTIFICATE-----
AAAAAAAAAAABBBBBBBBBBCCCCCC
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
DDDDDDDDDDDEEEEEEEEEEFFFFFF
-----END RSA PRIVATE KEY-----
เชเชพเชฒเซ dex-k8s-authenticator เชฐเซเชชเซเชเซเชเชฐเซเชจเซ เชเซเชฒเซเชจ เชเชฐเซเช:
git clone [email protected]:mintel/dex-k8s-authenticator.git
cd dex-k8s-authenticator/
เชฎเซเชฒเซเชฏเซเชจเซ เชซเชพเชเชฒเซเชจเซ เชเชชเชฏเซเช เชเชฐเซเชจเซ, เช
เชฎเซ เช
เชฎเชพเชฐเชพ เชฎเชพเชเซ เชเชฒเซเชจเซ เชฒเชตเชเซเช เชฐเซเชคเซ เชเซเช เชตเซ เชถเชเซเช เชเซเช
เชเชพเชฒเซ Dex เชฎเชพเชเซ เชฐเซเชชเชฐเซเชเชพเชเชเชจเชจเซเช เชตเชฐเซเชฃเชจ เชเชฐเซเช:
cat << EOF > values-dex.yml
global:
deployEnv: prod
tls:
certificate: |-
-----BEGIN CERTIFICATE-----
AAAAAAAAAAABBBBBBBBBBCCCCCC
-----END CERTIFICATE-----
key: |-
-----BEGIN RSA PRIVATE KEY-----
DDDDDDDDDDDEEEEEEEEEEFFFFFF
-----END RSA PRIVATE KEY-----
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- dex.k8s.example.com
tls:
- secretName: cert-auth-dex
hosts:
- dex.k8s.example.com
serviceAccount:
create: true
name: dex-auth-sa
config: |
issuer: https://dex.k8s.example.com/
storage: # https://github.com/dexidp/dex/issues/798
type: sqlite3
config:
file: /var/dex.db
web:
http: 0.0.0.0:5556
frontend:
theme: "coreos"
issuer: "Example Co"
issuerUrl: "https://example.com"
logoUrl: https://example.com/images/logo-250x25.png
expiry:
signingKeys: "6h"
idTokens: "24h"
logger:
level: debug
format: json
oauth2:
responseTypes: ["code", "token", "id_token"]
skipApprovalScreen: true
connectors:
- type: github
id: github
name: GitHub
config:
clientID: $GITHUB_CLIENT_ID
clientSecret: $GITHUB_CLIENT_SECRET
redirectURI: https://dex.k8s.example.com/callback
orgs:
- name: super-org
teams:
- team-red
staticClients:
- id: dex-k8s-authenticator
name: dex-k8s-authenticator
secret: generatedLongRandomPhrase
redirectURIs:
- https://login.k8s.example.com/callback/
envSecrets:
GITHUB_CLIENT_ID: "1ab2c3d4e5f6g7h8"
GITHUB_CLIENT_SECRET: "98z76y54x32w1"
EOF
เช เชจเซ dex-k8s-authenticator เชฎเชพเชเซ:
cat << EOF > values-auth.yml
global:
deployEnv: prod
dexK8sAuthenticator:
clusters:
- name: k8s.example.com
short_description: "k8s cluster"
description: "Kubernetes cluster"
issuer: https://dex.k8s.example.com/
k8s_master_uri: https://api.k8s.example.com
client_id: dex-k8s-authenticator
client_secret: generatedLongRandomPhrase
redirect_uri: https://login.k8s.example.com/callback/
k8s_ca_pem: |
-----BEGIN CERTIFICATE-----
AAAAAAAAAAABBBBBBBBBBCCCCCC
-----END CERTIFICATE-----
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- login.k8s.example.com
tls:
- secretName: cert-auth-login
hosts:
- login.k8s.example.com
EOF
เชกเซเชเซเชธ เช เชจเซ เชกเซเชเซเชธ-เชเซ8เชเชธ-เชเชฅเซเชจเซเชเชฟเชเซเชเชฐ เชเชจเซเชธเซเชเซเชฒ เชเชฐเซ:
helm install -n dex --namespace kube-system --values values-dex.yml charts/dex
helm install -n dex-auth --namespace kube-system --values values-auth.yml charts/dex-k8s-authenticator
เชเชพเชฒเซ เชธเซเชตเชพเชเชจเซ เชเชพเชฐเซเชฏเชเซเชทเชฎเชคเชพ เชคเชชเชพเชธเซเช (Dex เช เชเซเชก 400 เชชเชฐเชค เชเชฐเชตเซ เชเซเชเช, เช เชจเซ dex-k8s-เชเชฅเซเชจเซเชเชฟเชเซเชเชฐเซ เชเซเชก 200 เชชเชฐเชค เชเชฐเชตเซ เชเซเชเช):
curl -sI https://dex.k8s.example.com/callback | head -1
HTTP/2 400
curl -sI https://login.k8s.example.com/ | head -1
HTTP/2 200
RBAC เชฐเซเชชเชฐเซเชเชพเชเชเชจ
เช เชฎเซ เชเซเชฅ เชฎเชพเชเซ เชเซเชฒเชธเซเชเชฐเชฐเซเชฒ เชฌเชจเชพเชตเซเช เชเซเช, เช เชฎเชพเชฐเชพ เชเชฟเชธเซเชธเชพเชฎเชพเช เชซเชเซเชค เชตเชพเชเชเซ เชถเชเชพเชฏ เชคเซเชตเซ เชเชเซเชธเซเชธ เชธเชพเชฅเซ:
cat << EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-read-all
rules:
-
apiGroups:
- ""
- apps
- autoscaling
- batch
- extensions
- policy
- rbac.authorization.k8s.io
- storage.k8s.io
resources:
- componentstatuses
- configmaps
- cronjobs
- daemonsets
- deployments
- events
- endpoints
- horizontalpodautoscalers
- ingress
- ingresses
- jobs
- limitranges
- namespaces
- nodes
- pods
- pods/log
- pods/exec
- persistentvolumes
- persistentvolumeclaims
- resourcequotas
- replicasets
- replicationcontrollers
- serviceaccounts
- services
- statefulsets
- storageclasses
- clusterroles
- roles
verbs:
- get
- watch
- list
- nonResourceURLs: ["*"]
verbs:
- get
- watch
- list
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
EOF
เชเชพเชฒเซ ClusterRoleBinding เชฎเชพเชเซ เชฐเซเชชเชฐเซเชเชพเชเชเชจ เชฌเชจเชพเชตเซเช:
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: dex-cluster-auth
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-read-all
subjects:
kind: Group
name: "super-org:team-red"
EOF
เชนเชตเซ เช เชฎเซ เชชเชฐเซเชเซเชทเชฃ เชฎเชพเชเซ เชคเซเชฏเชพเชฐ เชเซเช.
เชชเชฐเซเชเซเชทเชฃเซ
เชฒเซเชเชฟเชจ เชชเซเช เชชเชฐ เชเชพเช (https://login.k8s.example.com
) เช
เชจเซ เชคเชฎเชพเชฐเชพ GitHub เชเชเชพเชเชจเซเชเชจเซ เชเชชเชฏเซเช เชเชฐเซเชจเซ เชฒเซเช เชเชจ เชเชฐเซ:
เชฒเซเชเชฟเชจ เชชเซเชทเซเช
เชฒเซเชเชฟเชจ เชชเซเชทเซเช เชจเซ GitHub เชชเชฐ เชฐเซเชกเชพเชฏเชฐเซเชเซเช เชเชฐเชตเชพเชฎเชพเช เชเชตเซเชฏเซเช
เชเชเซเชธเซเชธ เชฎเซเชณเชตเชตเชพ เชฎเชพเชเซ เชเชจเชฐเซเช เชเชฐเซเชฒเซ เชธเซเชเชจเชพเชเชจเซ เช
เชจเซเชธเชฐเซ
เชตเซเชฌ เชชเซเช เชชเชฐเชฅเซ เชเซเชชเซ-เชชเซเชธเซเช เชเชฐเซเชฏเชพ เชชเชเซ, เช เชฎเซ เช เชฎเชพเชฐเชพ เชเซเชฒเชธเซเชเชฐ เชธเชเชธเชพเชงเชจเซเชจเซเช เชธเชเชเชพเชฒเชจ เชเชฐเชตเชพ เชฎเชพเชเซ kubectl เชจเซ เชเชชเชฏเซเช เชเชฐเซ เชถเชเซเช เชเซเช:
kubectl get po
NAME READY STATUS RESTARTS AGE
mypod 1/1 Running 0 3d
kubectl delete po mypod
Error from server (Forbidden): pods "mypod" is forbidden: User "[email protected]" cannot delete pods in the namespace "default"
เช
เชจเซ เชคเซ เชเชพเชฎ เชเชฐเซ เชเซ, เช
เชฎเชพเชฐเซ เชธเชเชธเซเชฅเชพเชจเชพ เชคเชฎเชพเชฎ GitHub เชตเชชเชฐเชพเชถเชเชฐเซเชคเชพเช เชธเชเชธเชพเชงเชจเซ เชเซเช เชถเชเซ เชเซ เช
เชจเซ เชชเซเชกเซเชธเชฎเชพเช เชฒเซเช เชเชจ เชเชฐเซ เชถเชเซ เชเซ, เชชเชฐเชเชคเซ เชคเซเชฎเชจเซ เชชเชพเชธเซ เชคเซเชฎเชจเซ เชฌเชฆเชฒเชตเชพเชจเชพ เช
เชงเชฟเชเชพเชฐเซ เชจเชฅเซ.
เชธเซเชฐเซเชธ: www.habr.com