рд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ

рдпреЛ рд▓реЗрдЦ рдкрд╣рд┐рд▓реЗ рдиреИ рд╡рд┐рд╕реНрддрд╛рд░ рдЧрд░реНрди рд▓реЗрдЦрд┐рдПрдХреЛ рдЫ рдЕрд╡рд╕реНрдерд┐рдд, рддрд░ Microsoft ActiveDirectory рд╕рдБрдЧ рдмрдиреНрдбрд▓рдХрд╛ рд╕реБрд╡рд┐рдзрд╛рд╣рд░реВрдХреЛ рдмрд╛рд░реЗрдорд╛ рдХреБрд░рд╛ рдЧрд░реНрдЫ, рд░ рдпрд╕рд▓рд╛рдИ рдкреВрд░рдХ рдкрдирд┐ рдЧрд░реНрджрдЫред

рдпрд╕ рд▓реЗрдЦрдорд╛ рдо рддрдкрд╛рдИрдВрд▓рд╛рдИ рдХрд╕рд░реА рд╕реНрдерд╛рдкрдирд╛ рд░ рдХрдиреНрдлрд┐рдЧрд░ рдЧрд░реНрдиреЗ рдмрддрд╛рдЙрдиреЗрдЫреБ:

  • рдХрд┐рдХреНрд▓реЛрдХ рдЦреБрд▓рд╛ рд╕реНрд░реЛрдд рдкрд░рд┐рдпреЛрдЬрдирд╛ рд╣реЛред рдЬрд╕рд▓реЗ рдЕрдиреБрдкреНрд░рдпреЛрдЧрд╣рд░реВрдХреЛ рд▓рд╛рдЧрд┐ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдХреЛ рдПрдХрд▓ рдмрд┐рдиреНрджреБ рдкреНрд░рджрд╛рди рдЧрд░реНрджрдЫред LDAP рд░ OpenID рд▓рдЧрд╛рдпрддрдХрд╛ рдзреЗрд░реИ рдкреНрд░реЛрдЯреЛрдХрд▓рд╣рд░реВрд╕рдБрдЧ рдХрд╛рдо рдЧрд░реНрджрдЫ рдЬреБрди рд╣рд╛рдореА рд░реБрдЪрд┐ рд░рд╛рдЦреНрдЫреМрдВред
  • рдХрд┐рдХреНрд▓реЛрдХ рдЧреЗрдЯрдХреАрдкрд░ - рд░рд┐рднрд░реНрд╕ рдкреНрд░реЛрдХреНрд╕реА рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдЬрд╕рд▓реЗ рддрдкрд╛рдИрдВрд▓рд╛рдИ рдХреАрдХреНрд▓реЛрдХ рдорд╛рд░реНрдлрдд рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдПрдХреАрдХреГрдд рдЧрд░реНрди рдЕрдиреБрдорддрд┐ рджрд┐рдиреНрдЫред
  • рдЧреНрдпрд╛wayрд╡реЗ - рдПрдЙрдЯрд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдЬрд╕рд▓реЗ kubectl рдХреЛ рд▓рд╛рдЧрд┐ рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рди рдЙрддреНрдкрдиреНрди рдЧрд░реНрджрдЫ рдЬрд╕рдХреЛ рд╕рд╛рде рддрдкрд╛рдЗрдБ рд▓рдЧ рдЗрди рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ рд░ OpenID рдорд╛рд░реНрдлрдд Kubernetes API рдорд╛ рдЬрдбрд╛рди рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫред

Kubernetes рдорд╛ рдЕрдиреБрдорддрд┐рд╣рд░реВ рдХрд╕рд░реА рдХрд╛рдо рдЧрд░реНрджрдЫред

рд╣рд╛рдореА RBAC рдХреЛ рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛/рд╕рдореВрд╣ рдЕрдзрд┐рдХрд╛рд░рд╣рд░реВ рдкреНрд░рдмрдиреНрдз рдЧрд░реНрди рд╕рдХреНрдЫреМрдВ, рдпрд╕ рдмрд╛рд░реЗ рд▓реЗрдЦрд╣рд░реВрдХреЛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдкрд╣рд┐рд▓реЗ рдиреИ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░рд┐рдПрдХреЛ рдЫ, рдо рдпрд╕ рдмрд╛рд░реЗ рд╡рд┐рд╕реНрддреГрдд рд░реВрдкрдорд╛ рдзреНрдпрд╛рди рджрд┐рдиреЗ рдЫреИрдиред рд╕рдорд╕реНрдпрд╛ рдпреЛ рд╣реЛ рдХрд┐ рддрдкрд╛рдЗрдБ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛ рдЕрдзрд┐рдХрд╛рд░рд╣рд░реВ рдкреНрд░рддрд┐рдмрдиреНрдз рдЧрд░реНрди RBAC рдкреНрд░рдпреЛрдЧ рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ, рддрд░ Kubernetes рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛рд╣рд░реВрдХреЛ рдмрд╛рд░реЗрдорд╛ рдХреЗрд╣рд┐ рдерд╛рд╣рд╛ рдЫреИрдиред рдпреЛ рдмрд╛рд╣рд┐рд░ рдЬрд╛рдиреНрдЫ рдХрд┐ рд╣рд╛рдореАрд▓рд╛рдИ Kubernetes рдорд╛ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛ рд╡рд┐рддрд░рдг рд╕рдВрдпрдиреНрддреНрд░ рдЪрд╛рд╣рд┐рдиреНрдЫред рдпреЛ рдЧрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐, рд╣рд╛рдореА Kuberntes OpenID рдорд╛ рдПрдХ рдкреНрд░рджрд╛рдпрдХ рдердкреНрдиреЗрдЫреМрдВ, рдЬрд╕рд▓реЗ рдпрд╕реНрддреЛ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛ рд╡рд╛рд╕реНрддрд╡рдорд╛ рдЕрд╡рд╕реНрдерд┐рдд рдЫ рднрдиреЗрд░ рднрдиреНрдиреЗрдЫ, рд░ Kubernetes рдЖрдлреИрд▓реЗ рдЙрд╕рд▓рд╛рдИ рдЕрдзрд┐рдХрд╛рд░ рджрд┐рдиреЗрдЫред

рдкреНрд░рд╢рд┐рдХреНрд╖рдг

  • рддрдкрд╛рдИрдВрд▓рд╛рдИ Kubernetes рдХреНрд▓рд╕реНрдЯрд░ рд╡рд╛ minikube рдЪрд╛рд╣рд┐рдиреНрдЫ
  • рд╕рдХреНрд░рд┐рдп рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛
  • рдбреЛрдореЗрдирд╣рд░реВ:
    keycloak.example.org
    kubernetes-dashboard.example.org
    gangway.example.org
  • рдбреЛрдореЗрди рд╡рд╛ рд╕реНрд╡-рд╣рд╕реНрддрд╛рдХреНрд╖рд░рд┐рдд рдкреНрд░рдорд╛рдгрдкрддреНрд░рдХреЛ рд▓рд╛рдЧрд┐ рдкреНрд░рдорд╛рдгрдкрддреНрд░

рдо рдХрд╕рд░реА рд╕реНрд╡-рд╣рд╕реНрддрд╛рдХреНрд╖рд░рд┐рдд рдкреНрд░рдорд╛рдгрдкрддреНрд░ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреЗ рднрдиреНрдиреЗрдорд╛ рдзреНрдпрд╛рди рджрд┐рдиреЗ рдЫреИрди, рддрдкрд╛рдИрдВрд▓реЗ 2 рдкреНрд░рдорд╛рдгрдкрддреНрд░рд╣рд░реВ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫ, рдпреЛ *.example.org рдбреЛрдореЗрдирдХреЛ рд▓рд╛рдЧрд┐ рд░реВрдЯ (рдкреНрд░рдорд╛рдгрдкрддреНрд░ рдкреНрд░рд╛рдзрд┐рдХрд░рдг) рд░ рд╡рд╛рдЗрд▓реНрдбрдХрд╛рд░реНрдб рдХреНрд▓рд╛рдЗрдиреНрдЯ рд╣реЛред

рддрдкрд╛рдИрдВрд▓реЗ рдкреНрд░рдорд╛рдгрдкрддреНрд░рд╣рд░реВ рдкреНрд░рд╛рдкреНрдд / рдЬрд╛рд░реА рдЧрд░реЗрдкрдЫрд┐, рдЧреНрд░рд╛рд╣рдХрд▓рд╛рдИ Kubernetes рдорд╛ рдердкреНрдиреБ рдкрд░реНрдЫ, рдпрд╕рдХреЛ рд▓рд╛рдЧрд┐ рд╣рд╛рдореА рдпрд╕рдХреЛ рд▓рд╛рдЧрд┐ рдПрдЙрдЯрд╛ рдЧреЛрдкреНрдп рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдЫреМрдВ:

kubectl create secret tls tls-keycloak --cert=example.org.crt --key=example.org.pem

рдЕрд░реНрдХреЛ, рд╣рд╛рдореА рдпрд╕рд▓рд╛рдИ рд╣рд╛рдореНрд░реЛ рдкреНрд░рд╡реЗрд╢ рдирд┐рдпрдиреНрддреНрд░рдХрдХреЛ рд▓рд╛рдЧрд┐ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреЗрдЫреМрдВред

рдХреАрдХреНрд▓реЛрдХ рд╕реНрдерд╛рдкрдирд╛

рдореИрд▓реЗ рдирд┐рд░реНрдгрдп рдЧрд░реЗрдВ рдХрд┐ рд╕рдмреИрднрдиреНрджрд╛ рд╕рдЬрд┐рд▓реЛ рддрд░рд┐рдХрд╛ рдпрд╕рдХреЛ рд▓рд╛рдЧрд┐ рддрдпрд╛рд░ рд╕рдорд╛рдзрд╛рдирд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреЗ рд╣реЛ, рдЕрд░реНрдерд╛рддреН рд╣реЗрд▓реНрдо рдЪрд╛рд░реНрдЯрд╣рд░реВред

рднрдгреНрдбрд╛рд░ рд╕реНрдерд╛рдкрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН рд░ рдпрд╕рд▓рд╛рдИ рдЕрджреНрдпрд╛рд╡рдзрд┐рдХ рдЧрд░реНрдиреБрд╣реЛрд╕реН:

helm repo add codecentric https://codecentric.github.io/helm-charts
helm repo update

рдирд┐рдореНрди рд╕рд╛рдордЧреНрд░реАрдХреЛ рд╕рд╛рде keycloak.yml рдлрд╛рдЗрд▓ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН:

keycloak.yml

keycloak:
  # ╨Ш╨╝╤П ╨░╨┤╨╝╨╕╨╜╨╕╤Б╤В╤А╨░╤В╨╛╤А╨░
  username: "test_admin"
  # ╨Я╨░╤А╨╛╨╗╤М ╨░╨┤╨╝╨╕╨╜╨╕╤Б╤В╤А╨░╤В╨╛╤А  
  password: "admin"
  # ╨н╤В╨╕ ╤Д╨╗╨░╨│╨╕ ╨╜╤Г╨╢╨╜╤Л ╤З╤В╨╛ ╨▒╤Л ╨┐╨╛╨╖╨▓╨╛╨╗╨╕╤В╤М ╨╖╨░╨│╤А╤Г╨╢╨░╤В╤М ╨▓ Keycloak ╤Б╨║╤А╨╕╨┐╤В╤Л ╨┐╤А╤П╨╝╨╛ ╤З╨╡╤А╨╡╨╖ web ╨╝╨╛╤А╨┤╤Г. ╨н╤В╨╛ ╨╜╨░╨╝ 
  ╨┐╨╛╨╜╨░╨┤╨╛╨▒╨╕╤В╤М╤Б╤П ╤З╤В╨╛ ╨▒╤Л ╨┐╨╛╤З╨╕╨╜╨╕╤В╤М ╨╛╨┤╨╕╨╜ ╨▒╨░╨│, ╨╛ ╨║╨╛╤В╨╛╤А╨╛╨╝ ╨╜╨╕╨╢╨╡.
  extraArgs: "-Dkeycloak.profile.feature.script=enabled -Dkeycloak.profile.feature.upload_scripts=enabled" 
  # ╨Т╨║╨╗╤О╤З╨░╨╡╨╝ ingress, ╤Г╨║╨░╨╖╤Л╨▓╨░╨╡╨╝ ╨╕╨╝╤П ╤Е╨╛╤Б╤В╨░ ╨╕ ╤Б╨╡╤А╤В╨╕╤Д╨╕╨║╨░╤В ╨║╨╛╤В╨╛╤А╤Л╨╣ ╨╝╤Л ╨┐╤А╨╡╨┤╨▓╨░╤А╨╕╤В╨╡╨╗╤М╨╜╨╛ ╤Б╨╛╤Е╤А╨░╨╜╨╕╨╗╨╕ ╨▓ secrets
  ingress:
    enabled: true 
    path: /
    annotations:
      kubernetes.io/ingress.class: nginx
      ingress.kubernetes.io/affinity: cookie
    hosts:
      - keycloak.example.org
    tls:
    - hosts:
        - keycloak.example.org
      secretName: tls-keycloak
  # Keycloak ╨┤╨╗╤П ╤Б╨▓╨╛╨╡╨╣ ╤А╨░╨▒╨╛╤В╤Л ╤В╤А╨╡╨▒╤Г╨╡╤В ╨▒╨░╨╖╤Г ╨┤╨░╨╜╨╜╤Л╤Е, ╨▓ ╤В╨╡╤Б╤В╨╛╨▓╤Л╤Е ╤Ж╨╡╨╗╤П╤Е ╤П ╤А╨░╨╖╨▓╨╛╤А╨░╤З╨╕╨▓╨░╤О Postgresql ╨┐╤А╤П╨╝╨╛ ╨▓ Kuberntes, ╨▓ ╨┐╤А╨╛╨┤╨░╨║╤И╨╡╨╜╨╡ ╤В╨░╨║ ╨╗╤Г╤З╤И╨╡ ╨╜╨╡ ╨┤╨╡╨╗╨░╤В╤М!
  persistence:
    deployPostgres: true
    dbVendor: postgres

postgresql:
  postgresUser: keycloak
  postgresPassword: ""
  postgresDatabase: keycloak
  persistence:
    enabled: true

рдорд╣рд╛рд╕рдВрдШ рд╕реНрдерд╛рдкрдирд╛

рдЕрд░реНрдХреЛ, рд╡реЗрдм рдЗрдиреНрдЯрд░рдлреЗрд╕рдорд╛ рдЬрд╛рдиреБрд╣реЛрд╕реН keycloak.example.org

рдмрд╛рдпрд╛рдБ рдХреБрдирд╛рдорд╛ рдХреНрд▓рд┐рдХ рдЧрд░реНрдиреБрд╣реЛрд╕реН рдХреНрд╖реЗрддреНрд░ рдердкреНрдиреБрд╣реЛрд╕реН

рдкреНрд░рдореБрдЦ
рдореВрд▓реНрдп

рдирд╛рдо
рдХреБрдмреЗрд░рдиреЗрдЯреНрд╕

рдкреНрд░рджрд░реНрд╢рди рдирд╛рдо
рдХреБрдмрд░реНрдиреЗрдЯреНрд╕

рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛ рдЗрдореЗрд▓ рдкреНрд░рдорд╛рдгрд┐рдХрд░рдг рдЕрд╕рдХреНрд╖рдо рдЧрд░реНрдиреБрд╣реЛрд╕реН:
рдХреНрд▓рд╛рдЗрдиреНрдЯ рд╕реНрдХреЛрдкрд╣рд░реВ тАФ> рдЗрдореЗрд▓ тАФ> рдореНрдпрд╛рдкрд░реНрд╕ тАФ> рдЗрдореЗрд▓ рдкреНрд░рдорд╛рдгрд┐рдд (рдореЗрдЯреНрдиреБрд╣реЛрд╕реН)

рд╣рд╛рдореАрд▓реЗ ActiveDirectory рдмрд╛рдЯ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛рд╣рд░реВ рдЖрдпрд╛рдд рдЧрд░реНрди рдлреЗрдбрд░реЗрд╢рди рд╕реЗрдЯ рдЕрдк рдЧрд░реНрдпреМрдВ, рдо рддрд▓ рд╕реНрдХреНрд░рд┐рдирд╕рдЯрд╣рд░реВ рдЫреЛрдбреНрдиреЗрдЫреБ, рдорд▓рд╛рдИ рд▓рд╛рдЧреНрдЫ рдХрд┐ рдпреЛ рд╕реНрдкрд╖реНрдЯ рд╣реБрдиреЗрдЫред

рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛ рд╕рдВрдШ -> рдкреНрд░рджрд╛рдпрдХ рдердкреНрдиреБрд╣реЛрд╕реН ... -> ldap

рдорд╣рд╛рд╕рдВрдШ рд╕реНрдерд╛рдкрдирд╛рд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ
рд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ

рдпрджрд┐ рд╕рдмреИ рдареАрдХ рдЫ рднрдиреЗ, рдмрдЯрди рдерд┐рдЪреЗ рдкрдЫрд┐ рд╕рдмреИ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛рд╣рд░реВ рд╕рд┐рдЩреНрдХреНрд░реЛрдирд╛рдЗрдЬ рдЧрд░реНрдиреБрд╣реЛрд╕реН рддрдкрд╛рдИрдВрд▓реЗ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛рд╣рд░реВрдХреЛ рд╕рдлрд▓ рдЖрдпрд╛рддрдХреЛ рдмрд╛рд░реЗрдорд╛ рд╕рдиреНрджреЗрд╢ рджреЗрдЦреНрдиреБрд╣реБрдиреЗрдЫред

рдЕрд░реНрдХреЛ рд╣рд╛рдореАрд▓реЗ рд╣рд╛рдореНрд░реЛ рд╕рдореВрд╣рд╣рд░реВ рдирдХреНрд╕рд╛ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫ

рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛ рд╕рдВрдШ --> ldap_localhost --> рдореНрдпрд╛рдкрд░ --> рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН

рдореНрдпрд╛рдкрд░ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрджреИрд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ

рдЧреНрд░рд╛рд╣рдХ рд╕реЗрдЯрдЕрдк

рдпреЛ рдПрдХ рдЧреНрд░рд╛рд╣рдХ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫ, Keycloak рдХреЛ рд╕рд░реНрддрдорд╛, рдпреЛ рдЙрд╣рд╛рдБрдмрд╛рдЯ рдЕрдзрд┐рдХреГрдд рдЧрд░рд┐рдиреЗрдЫ рдХрд┐ рдПрдХ рдЖрд╡реЗрджрди рдЫред рдо рд░рд╛рддреЛрдорд╛ рд╕реНрдХреНрд░рд┐рдирд╕рдЯрдорд╛ рдорд╣рддреНрддреНрд╡рдкреВрд░реНрдг рдмрд┐рдиреНрджреБрд╣рд░реВ рд╣рд╛рдЗрд▓рд╛рдЗрдЯ рдЧрд░реНрдиреЗрдЫреБред

рдЧреНрд░рд╛рд╣рдХрд╣рд░реВ -> рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН

рдЧреНрд░рд╛рд╣рдХ рд╕реЗрдЯрдЕрдкрд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ

рд╕рдореВрд╣рд╣рд░реВрдХреЛ рд▓рд╛рдЧрд┐ рд╕реНрдХреВрдк рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реМрдВ:

рдЧреНрд░рд╛рд╣рдХ рджрд╛рдпрд░рд╛ -> рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН

рджрд╛рдпрд░рд╛ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реНрд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ

рд░ рддрд┐рдиреАрд╣рд░реВрдХрд╛ рд▓рд╛рдЧрд┐ рдореНрдпрд╛рдкрд░ рд╕реЗрдЯ рдЕрдк рдЧрд░реНрдиреБрд╣реЛрд╕реН:

рдХреНрд▓рд╛рдЗрдиреНрдЯ рд╕реНрдХреЛрдк -> рд╕рдореВрд╣рд╣рд░реВ -> рдореНрдпрд╛рдкрд░рд╣рд░реВ -> рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН

рдореНрдпрд╛рдкрд░рд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ

рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХреНрд▓рд╛рдЗрдиреНрдЯ рд╕реНрдХреЛрдкрд╣рд░реВрдорд╛ рд╣рд╛рдореНрд░рд╛ рд╕рдореВрд╣рд╣рд░реВрдХреЛ рдореНрдпрд╛рдкрд┐рдЩ рдердкреНрдиреБрд╣реЛрд╕реН:

рдХреНрд▓рд╛рдЗрдиреНрдЯрд╣рд░реВ тАФ> kubernetes тАФ> рдХреНрд▓рд╛рдЗрдиреНрдЯ рд╕реНрдХреЛрдкрд╣рд░реВ тАФ> рдбрд┐рдлрд▓реНрдЯ рдХреНрд▓рд╛рдЗрдиреНрдЯ рд╕реНрдХреЛрдкрд╣рд░реВ
рдЫрдиреМрдЯ рдЧрд░реНрдиреБрд╣реЛрд╕реН рд╕рдореВрд╣ ╨▓ рдЙрдкрд▓рдмреНрдз рдЧреНрд░рд╛рд╣рдХ рджрд╛рдпрд░рд╛рдХреНрд▓рд┐рдХ рдЧрд░реНрдиреБрд╣реЛрд╕реН рдЪрдпрди рдЧрд░рд┐рдПрдХреЛ рдердкреНрдиреБрд╣реЛрд╕реН

рд╣рд╛рдореАрд▓реЗ рдЧреЛрдкреНрдп рдкрд╛рдЙрдБрдЫреМрдВ (рд░ рдпрд╕рд▓рд╛рдИ рдереНрд░реЗрдбрдорд╛ рд▓реЗрдЦреНрдЫреМрдВ) рдЬреБрди рд╣рд╛рдореАрд▓реЗ рдХрд┐рдХреНрд▓реЛрдХрдорд╛ рдкреНрд░рд╛рдзрд┐рдХрд░рдгрдХреЛ рд▓рд╛рдЧрд┐ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреЗрдЫреМрдВ:

рдЧреНрд░рд╛рд╣рдХрд╣рд░реВ тАФ> kubernetes тАФ> рдкреНрд░рдорд╛рдгрд╣рд░реВ тАФ> рдЧреЛрдкреНрдп
рдпрд╕рд▓реЗ рд╕реЗрдЯрдЕрдк рдкреВрд░рд╛ рдЧрд░реНрдЫ, рддрд░ рдорд╕рдБрдЧ рддреНрд░реБрдЯрд┐ рднрдпреЛ рдЬрдм, рд╕рдлрд▓ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдкрдЫрд┐, рдореИрд▓реЗ рддреНрд░реБрдЯрд┐ 403 рдкреНрд░рд╛рдкреНрдд рдЧрд░реЗрдВред рдмрдЧ рд░рд┐рдкреЛрд░реНрдЯ.

рд╕рдорд╛рдзрд╛рди:

рдХреНрд▓рд╛рдЗрдиреНрдЯ рд╕реНрдХреЛрдк -> рднреВрдорд┐рдХрд╛рд╣рд░реВ -> рдореНрдпрд╛рдкрд░рд╣рд░реВ -> рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН

рдореНрдпрд╛рдкрд░рд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ

рд▓рд┐рдкрд┐ рдХреЛрдб

// add current client-id to token audience
token.addAudience(token.getIssuedFor());

// return token issuer as dummy result assigned to iss again
token.getIssuer();

Kubernetes рдХрдиреНрдлрд┐рдЧрд░ рдЧрд░реНрджреИ

рд╣рд╛рдореАрд▓реЗ рд╕рд╛рдЗрдЯрдмрд╛рдЯ рд╣рд╛рдореНрд░реЛ рдореВрд▓ рдкреНрд░рдорд╛рдгрдкрддреНрд░ рдХрд╣рд╛рдБ рдЫ, рд░ OIDC рдкреНрд░рджрд╛рдпрдХ рдХрд╣рд╛рдБ рдЫ рднрдиреЗрд░ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫред
рдпреЛ рдЧрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐, рдлрд╛рдЗрд▓ рд╕рдореНрдкрд╛рджрди рдЧрд░реНрдиреБрд╣реЛрд╕реН /etc/kubernetes/manifests/kube-apiserver.yaml

kube-apiserver.yaml


...
spec:
  containers:
  - command:
    - kube-apiserver
...
    - --oidc-ca-file=/var/lib/minikube/certs/My_Root.crt
    - --oidc-client-id=kubernetes
    - --oidc-groups-claim=groups
    - --oidc-issuer-url=https://keycloak.example.org/auth/realms/kubernetes
    - --oidc-username-claim=email
...

рдХреНрд▓рд╕реНрдЯрд░рдорд╛ kubeadm рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рди рдЕрдкрдбреЗрдЯ рдЧрд░реНрдиреБрд╣реЛрд╕реН:

kubeadmconfig

kubectl edit -n kube-system configmaps kubeadm-config


...
data:
  ClusterConfiguration: |
    apiServer:
      extraArgs:
        oidc-ca-file: /var/lib/minikube/certs/My_Root.crt
        oidc-client-id: kubernetes
        oidc-groups-claim: groups
        oidc-issuer-url: https://keycloak.example.org/auth/realms/kubernetes
        oidc-username-claim: email
...

рдкреНрд░рдорд╛рдгреАрдХрд░рдг-рдкреНрд░реЛрдХреНрд╕реА рд╕реЗрдЯ рдЧрд░реНрджреИ

рддрдкрд╛рдЗрдБ рддрдкрд╛рдЗрдБрдХреЛ рд╡реЗрдм рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдХреЛ рд╕реБрд░рдХреНрд╖рд╛ рдЧрд░реНрди рдХрд┐рдХреНрд▓реЛрдХ рдЧреЗрдЯрдХреАрдкрд░ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫред рдпрд╕ рд░рд┐рднрд░реНрд╕ рдкреНрд░реЛрдХреНрд╕реАрд▓реЗ рдкреГрд╖реНрда рджреЗрдЦрд╛рдЙрдиреБ рдЕрдШрд┐ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛рд▓рд╛рдИ рдЕрдзрд┐рдХрд╛рд░ рджрд┐рдиреЗрдЫ рднрдиреНрдиреЗ рддрдереНрдпрдХреЛ рдЕрддрд┐рд░рд┐рдХреНрдд, рдпрд╕рд▓реЗ рд╣реЗрдбрд░рд╣рд░реВрдорд╛ рдЕрдиреНрддрд┐рдо рдЕрдиреБрдкреНрд░рдпреЛрдЧрдорд╛ рддрдкрд╛рдИрдВрдХреЛ рдмрд╛рд░реЗрдорд╛ рдЬрд╛рдирдХрд╛рд░реА рдкрдирд┐ рдкрдард╛рдЙрдиреЗрдЫред рддрд╕рд░реНрде, рдпрджрд┐ рддрдкрд╛рдЗрдБрдХреЛ рдЕрдиреБрдкреНрд░рдпреЛрдЧрд▓реЗ OpenID рд▓рд╛рдИ рд╕рдорд░реНрдерди рдЧрд░реНрджрдЫ рднрдиреЗ, рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛ рддреБрд░реБрдиреНрддреИ рдЕрдзрд┐рдХреГрдд рд╣реБрдиреНрдЫред Kubernetes Dashboard рдХреЛ рдЙрджрд╛рд╣рд░рдгрд▓рд╛рдИ рд╡рд┐рдЪрд╛рд░ рдЧрд░реНрдиреБрд╣реЛрд╕реН

Kubernetes рдбреНрдпрд╛рд╕рдмреЛрд░реНрдб рд╕реНрдерд╛рдкрдирд╛ рдЧрд░реНрджреИ


helm install stable/kubernetes-dashboard --name dashboard -f values_dashboard.yaml

values_dashboard.yaml

enableInsecureLogin: true
service:
  externalPort: 80
rbac:
  clusterAdminRole: true
  create: true
serviceAccount:
  create: true
  name: 'dashboard-test'

рдкрд╣реБрдБрдЪ рдЕрдзрд┐рдХрд╛рд░ рд╕реЗрдЯ рдЧрд░реНрджреИ:

ClusterRoleBinding рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реМрдВ рдЬрд╕рд▓реЗ DataOPS рд╕рдореВрд╣рдХрд╛ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛рд╣рд░реВрд▓рд╛рдИ рдХреНрд▓рд╕реНрдЯрд░ рдкреНрд░рд╢рд╛рд╕рдХ рдЕрдзрд┐рдХрд╛рд░рд╣рд░реВ (рдорд╛рдирдХ рдХреНрд▓рд╕реНрдЯрд░рд░реЛрд▓ рдХреНрд▓рд╕реНрдЯрд░-рдкреНрд░рд╢рд╛рд╕рдХ) рджрд┐рдиреЗрдЫред


kubectl apply -f rbac.yaml

rbac.yaml


apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dataops_group
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: DataOPS

рдХреАрдХреНрд▓реЛрдХ рдЧреЗрдЯрдХреАрдкрд░ рд╕реНрдерд╛рдкрдирд╛ рдЧрд░реНрдиреБрд╣реЛрд╕реН:


helm repo add gabibbo97 https://gabibbo97.github.io/charts/
helm repo update
helm install gabibbo97/keycloak-gatekeeper --version 2.1.0 --name keycloak-gatekeeper -f values_proxy.yaml

values_proxy.yaml



# ╨Т╨║╨╗╤О╤З╨░╨╡╨╝ ingress
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
  path: /
  hosts:
    - kubernetes-dashboard.example.org
  tls:
   - secretName: tls-keycloak
     hosts:
       - kubernetes-dashboard.example.org

# ╨У╨╛╨▓╨╛╤А╨╕╨╝ ╨│╨┤╨╡ ╨╝╤Л ╨▒╤Г╨┤╨╡╨╝ ╨░╨▓╤В╨╛╤А╨╕╨╖╨╛╨▓╤Л╨▓╨░╤В╤М╤Б╤П ╤Г OIDC ╨┐╤А╨╛╨▓╨░╨╣╨┤╨╡╤А╨░
discoveryURL: "https://keycloak.example.org/auth/realms/kubernetes"
# ╨Ш╨╝╤П ╨║╨╗╨╕╨╡╨╜╤В╨░ ╨║╨╛╤В╨╛╤А╨╛╨│╨╛ ╨╝╤Л ╤Б╨╛╨╖╨┤╨░╨╗╨╕ ╨▓ Keycloak
ClientID: "kubernetes"
# Secret ╨║╨╛╤В╨╛╤А╤Л╨╣ ╤П ╨┐╤А╨╛╤Б╨╕╨╗ ╨╖╨░╨┐╨╕╤Б╨░╤В╤М
ClientSecret: "c6ec03b8-d0b8-4cb6-97a0-03becba1d727"
# ╨Ъ╤Г╨┤╨░ ╨┐╨╡╤А╨╡╨╜╨░╨┐╤А╨░╨▓╨╕╤В╤М ╨▓ ╤Б╨╗╤Г╤З╨░╨╡ ╤Г╤Б╨┐╨╡╤И╨╜╨╛╨╣ ╨░╨▓╤В╨╛╤А╨╕╨╖╨░╤Ж╨╕╨╕. ╨д╨╛╤А╨╝╨░╤В <SCHEMA>://<SERVICE_NAME>.><NAMESAPCE>.<CLUSTER_NAME>
upstreamURL: "http://dashboard-kubernetes-dashboard.default.svc.cluster.local"
# ╨Я╤А╨╛╨┐╤Г╤Б╨║╨░╨╡╨╝ ╨┐╤А╨╛╨▓╨╡╤А╨║╤Г ╤Б╨╡╤А╤В╨╕╤Д╨╕╨║╨░╤В╨░, ╨╡╤Б╨╗╨╕ ╤Г ╨╜╨░╤Б ╤Б╨░╨╝╨╛╨┐╨╛╨┤╨┐╨╕╤Б╨░╨╜╨╜╤Л╨╣
skipOpenidProviderTlsVerify: true
# ╨Э╨░╤Б╤В╤А╨╛╨╣╨║╨░ ╨┐╤А╨░╨▓ ╨┤╨╛╤Б╤В╤Г╨┐╨░, ╨┐╤Г╤Б╨║╨░╨╡╨╝ ╨╜╨░ ╨▓╤Б╨╡ path ╨╡╤Б╨╗╨╕ ╨╝╤Л ╨▓ ╨│╤А╤Г╨┐╨┐╨╡ DataOPS
rules:
  - "uri=/*|groups=DataOPS"

рддреНрдпрд╕ рдкрдЫрд┐, рдЬрдм рддрдкрд╛рдИрдВ рдЬрд╛рдиреЗ рдкреНрд░рдпрд╛рд╕ рдЧрд░реНрдиреБрд╣реБрдиреНрдЫ kubernetes-dashboard.example.org, рд╣рд╛рдореАрд▓рд╛рдИ рдХрд┐рдХреНрд▓реЛрдХрдорд╛ рд░рд┐рдбрд┐рд░реЗрдХреНрдЯ рдЧрд░рд┐рдиреЗрдЫ рд░ рд╕рдлрд▓ рдкреНрд░рд╛рдзрд┐рдХрд░рдгрдХреЛ рдЕрд╡рд╕реНрдерд╛рдорд╛ рд╣рд╛рдореА рдкрд╣рд┐рд▓реЗ рдиреИ рд▓рдЧ рдЗрди рдЧрд░рд┐рдПрдХреЛ рдбреНрдпрд╛рд╕рдмреЛрд░реНрдбрдорд╛ рдкреБрдЧреНрдиреЗрдЫреМрдВред

gangway рд╕реНрдерд╛рдкрдирд╛

рд╕реБрд╡рд┐рдзрд╛рдХреЛ рд▓рд╛рдЧрд┐, рддрдкрд╛рдЗрдБ рдПрдЙрдЯрд╛ рдЧреНрдпрд╛рдЩреНрд╡реЗ рдердкреНрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ рдЬрд╕рд▓реЗ kubectl рдХреЛ рд▓рд╛рдЧреА рдХрдиреНрдлрд┐рдЧ рдлрд╛рдЗрд▓ рдЙрддреНрдкрдиреНрди рдЧрд░реНрджрдЫ, рдЬрд╕рдХреЛ рдорджреНрджрддрд▓реЗ рд╣рд╛рдореА рд╣рд╛рдореНрд░реЛ рдкреНрд░рдпреЛрдЧрдХрд░реНрддрд╛ рдЕрдиреНрддрд░реНрдЧрдд Kubernetes рдорд╛ рдкреНрд░рд╡реЗрд╢ рдЧрд░реНрдиреЗрдЫреМрдВред


helm install --name gangway stable/gangway -f values_gangway.yaml

values_gangway.yaml


gangway:
  # ╨Я╤А╨╛╨╕╨╖╨▓╨╛╨╗╤М╨╜╨╛╨╡ ╨╕╨╝╤П ╨║╨╗╨░╤Б╤В╨╡╤А╨░
  clusterName: "my-k8s"
  # ╨У╨┤╨╡ ╤Г ╨╜╨░╤Б OIDC ╨┐╤А╨╛╨▓╨░╨╣╨┤╨╡╤А
  authorizeURL: "https://keycloak.example.org/auth/realms/kubernetes/protocol/openid-connect/auth"
  tokenURL: "https://keycloak.example.org/auth/realms/kubernetes/protocol/openid-connect/token"
  audience: "https://keycloak.example.org/auth/realms/kubernetes/protocol/openid-connect/userinfo"
  # ╨в╨╡╨╛╤А╨╕╤В╨╕╤З╨╡╤Б╨║╨╕ ╤Б╤О╨┤╨░ ╨╝╨╛╨╢╨╜╨╛ ╨┤╨╛╨▒╨░╨▓╨╕╤В╤М groups ╨║╨╛╤В╨╛╤А╤Л╨╡ ╨╝╤Л ╨╖╨░╨╝╨░╨┐╨╕╨╗╨╕
  scopes: ["openid", "profile", "email", "offline_access"]
  redirectURL: "https://gangway.example.org/callback"
  # ╨Ш╨╝╤П ╨║╨╗╨╕╨╡╨╜╤В╨░
  clientID: "kubernetes"
  # ╨б╨╡╨║╤А╨╡╤В
  clientSecret: "c6ec03b8-d0b8-4cb6-97a0-03becba1d727"
  # ╨Х╤Б╨╗╨╕ ╨╛╤Б╤В╨░╨▓╨╕╤В╤М ╨┤╨╡╤Д╨╛╨╗╤В╨╜╨╛╨╡ ╨╖╨╜╨░╤З╨╜╨╕╨╡, ╤В╨╛ ╨╖╨░ ╨╕╨╝╤П ╨┐╨╛╨╗╤М╨╖╨╛╨▓╨░╤В╨╡╨╗╤П ╨▒╤Г╨┤╨╡╤В ╨▒╤А╨░╤В╤М╤П <b>Frist name</b> <b>Second name</b>, ╨░ ╨┐╤А╨╕ "sub" ╨╡╨│╨╛ ╨╗╨╛╨│╨╕╨╜
  usernameClaim: "sub"
  # ╨Ф╨╛╨╝╨╡╨╜╨╜╨╛╨╡ ╨╕╨╝╤П ╨╕╨╗╨╕ IP ╨░╨┤╤А╨╡╤Б╤Б API ╤Б╨╡╤А╨▓╨╡╤А╨░
  apiServerURL: "https://192.168.99.111:8443"

# ╨Т╨║╨╗╤О╤З╨░╨╡╨╝ Ingress
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-buffer-size: "64k"
  path: /
  hosts:
  - gangway.example.org
  tls:
  - secretName: tls-keycloak
    hosts:
      - gangway.example.org

# ╨Х╤Б╨╗╨╕ ╨╕╤Б╨┐╨╛╨╗╤М╨╖╤Г╨╡╨╝ ╤Б╨░╨╝╨╛╨┐╨╛╨┤╨┐╨╕╤Б╨░╨╜╨╜╤Л╨╣ ╤Б╨╡╤А╤В╨╕╤Д╨╕╨║╨░╤В, ╤В╨╛ ╨╡╨│╨╛(╨╛╤В╨║╤А╤Л╤В╤Л╨╣ ╨║╨╛╤А╨╜╨╡╨▓╨╛╨╣ ╤Б╨╡╤А╤В╨╕╤Д╨╕╨║╨░╤В) ╨╜╨░╨┤╨╛ ╤Г╨║╨░╨╖╨░╤В╤М.
trustedCACert: |-
 -----BEGIN CERTIFICATE-----
 MIIDVzCCAj+gAwIBAgIBATANBgkqhkiG9w0BAQsFADA1MQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRGF0YU9QUzEUMBIGA1UEAxMLbXkgcm9vdCBrZXkwHhcNMjAwMjE0MDkxODAwWhcNMzAwMjE0MDkxODAwWjA1MQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRGF0YU9QUzEUMBIGA1UEAxMLbXkgcm9vdCBrZXkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDyP749PqqIRwNSqaK6qr0Zsi03G4PTCUlgaYTPZuMrwUVPK8xX2dWWs9MPRMOdXpgr8aSTZnVfmelIlVz4D7o2vK5rfmAe9GPcK0WbwKwXyhFU0flS9sU/g46ogHFrk03SZxQAeJhMLfEmAJm8LF5HghtGDs3t4uwGsB95o+lqPLiBvxRB8ZS3jSpYpvPgXAuZWKdZUQ3UUZf0X3hGLp7uIcIwJ7i4MduOGaQEO4cePeEJy9aDAO6qV78YmHbyh9kaW+1DL/Sgq8NmTgHGV6UOnAPKHTnMKXl6KkyUz8uLBGIdVhPxrlzG1EzXresJbJenSZ+FZqm3oLqZbw54Yp5hAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHISTOU/6BQqqnOZj+1xJfxpjiG0MAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAAcwHgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAQEAj7HC8ObibwOLT4ZYmISJZwub9lcE0AZ5cWkPW39j/syhdbbqjK/6jy2D3WUEbR+s1Vson5Ov7JhN5In2yfZ/ByDvBnoj7CP8Q/ZMjTJgwN7j0rgmEb3CTZvnDPAz8Ijw3FP0cjxfoZ1Z0V2F44Ry7gtLJWr06+MztXVyto3aIz1/XbMQnXYlzc3c3B5yUQIy44Ce5aLRVsAjmXNqVRmDJ2QPNLicvrhnUJsO0zFWI+zZ2hc4Ge1RotCrjfOc9hQY63jZJ17myCZ6QCD7yzMzAob4vrgmkD4q7tpGrhPY/gDcE+lUNhC7DO3l0oPy2wsnT2TEn87eyWmDiTFG9zWDew==
 -----END CERTIFICATE-----

рдпрд╕реНрддреЛ рджреЗрдЦрд┐рдиреНрдЫред рддрдкрд╛рдИрдВрд▓рд╛рдИ рддреБрд░реБрдиреНрддреИ рдХрдиреНрдлрд┐рдЧ рдлрд╛рдЗрд▓ рдбрд╛рдЙрдирд▓реЛрдб рдЧрд░реНрди рд░ рдЖрджреЗрд╢рд╣рд░реВрдХреЛ рд╕реЗрдЯ рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдЙрддреНрдкрдиреНрди рдЧрд░реНрди рдЕрдиреБрдорддрд┐ рджрд┐рдиреНрдЫ:

рд╣рд╛рдореА Keycloak рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ Kubernetes рдХреЛ ActiveDirectory рдкреНрд░рд╛рдзрд┐рдХрд░рдгрд▓рд╛рдИ рдЬреЛрдбреНрджрдЫреМрдВ

рд╕реНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдердкреНрди