ใช้ Signed SSH Certificates ไว้ login ดีกว่า สบายกว่าเยอะ ep. 2

การเริ่มต้นนำเสนอรูปแบบการพิสูจน์ตัวตน เพื่อใช้งาน SSH Server โดยใช้ SSH Signed Certificate

author image
drs

Technology evangelistic advocacy

Posted on 2023-02-01 08:14:30 +0700

จากตอนแรก ใช้ Signed SSH Certificates ไว้ login ดีกว่า สบายกว่าเยอะ ep. 1 เป็นการเริ่มต้นนำเสนอรูปแบบการพิสูจน์ตัวตน เพื่อใช้งาน SSH Server โดยใช้ SSH Signed Certificate โดยหลักการ ก็คือ

  1. สร้าง Key Pair เพื่อทำหน้าที่เป็น Certificate Authority
  2. นำ Public Key ของ Certificate Authority ไปเก็บไว้ตาม SSH Server และ แก้ไข configuration ของ SSH Server ให้ trust ตัว Public Key ขอ Certificate Authority ที่สร้างในข้อ 1 และ PubkeyAuthentication ต้องมีค่าเป็น yes
  3. สร้าง user ที่จะเข้าใช้งานที่ server ตามปกติ โดยไม่จำเป็นต้องกำหนดรหัสผ่านใด ๆ
  4. ผู้ใช้งาน สร้าง key pair ของตัวเองขึ้นมา และ นำ Public key ไปทำการ signed ด้วย Key ของ Certificate Authority
  5. นำ Public Key ที่ถูก signed ไปใช้คู่กับ private key ที่สร้างในข้อ 4 ในการ login ไปยัง SSH Server

จากข้อสังเกตุในตอนแรก

จะเห็นว่า ขั้นตอนการ signed ที่ต้องการทำขั้นตอนนี้เป็นประจำ จำเป็นต้องใช้ Key ของ CA ซึ่งจะทำให้ขั้นตอนนี้ไม่สะดวกในมุมของการทำ Daily Operation นอกจากจะไม่สะดวกในการทำ daily operation ในการอนุญาตให้ผู้ใช้งาน เข้าใช้ server ในองค์กรแล้ว ก็ดูแลความปลอดภัย Private Key ของ CA เองก็เป็นเรื่องสำคัญ เพราะคนที่ได้ Private Key นี้ไป จะสามารถควบคุมการใช้งานของผู้ใช้งานที่จะเข้าถึง server ในองค์กรได้เลย

HashiCorp Vault ช่วยแก้ปัญหานี้ได้ มี Secrets Engine ที่ชื่อว่า  SSH Secrets Engine ช่วยจัดการ เก็บ Private Key ไว้ให้ จัดการ Signed ให้ ซึ่งก็จะทำหน้าที่ในขึ้นตอนที่ 1 และ 4 นอกจากนั้น HashiCorp Vault ยังมีส่วนที่ทำหน้าที่ Authentication และ Authorization ให้ด้วย ก็จะทำให้สามารถ ให้สิทธิ์กับแอดมินในการ signed ได้อีกด้วย

ออกตัวก่อนในตอนนี้ จะนำเสนอแนวคิดในการใช้ SSH Secrets Engine ที่จะทำหน้าที่ signed ให้กับ SSH Certificate เท่านั้น ขั้นตอนที่เป็น workflow ในการทำงานจริงจะนำเสนอในตอนถัดไป

#0 สมมุติฐานในเบื้องต้น

  • สมมุติฐานก่อนที่จะไปเรียนรู้วิธีการจัดเก็บความลับ ต้องติดตั้ง HashiCorp Vault, Unseal เรียบร้อยแล้ว และมี Root Token อยู่แล้ว ถ้ายังไม่คุ้นเคย ลองดูเรื่องนี้กันก่อนนะครับ หัดเล่น Vault แบบขี้เกียจ ใช้ Docker ละกัน
  • ในตอนนี้จะใช้ Root Token ในการทำงานกับ HashiCorp Vault
  • เครื่อง SSH Client ที่ใช้ในการทดสอบชื่อเครื่อง d8k-a2m2 ระบบปฏิบัติการเป็น macOS 13.2 ติดตั้ง vault v1.22.2 ทำหน้าที่เป็น HashiCorp Vault client ด้วย
  • เครื่อง SSH Server ที่ใช้ในการทดสอบชื่อเครื่อง servera.d8k.io และ serverb.d8k.io ระบบปฏิบัติการเป็น Ubuntu 22.04 LTS เป็น droplet ใน DigitalOcean
  • HashiCorp Vault ทำงานบน Docker Desktop 4.15.0 (93002) สำหรับ macOS

#1 เปิดการใช้งาน SSH Secrets Engine ที่ path ssh-signer/

คำสั่ง vault secrets เป็นคำสั่งที่ใช้ในการ enable ตัว SSH Secets Engine

d8k-a2m2 myvault  > vault login $TOKEN
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                hvs.RqXEYn8wngP2DSOiceMQAns0
token_accessor       FQSj3zLFIv6zayWteiYMpnmE
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

d8k-a2m2 myvault  > vault secrets enable -path=ssh-signer ssh
Success! Enabled the ssh secrets engine at: ssh-signer/

#2 สร้าง key pair เพื่อทำหน้าที่เป็น Certficate Authority

ในขั้นตอนนี้ จะสั่งให้ Vault สร้าง Key Pair เพื่อทำหน้าที่เป็น Certficate Authority และเก็บไว้ใน storage ของ Vault เอง จากนั้นก็นำ public key ออกมาเก็บไว้ที่ pki/trusted-user-ca-keys.pem เพื่อใช้งานต่อไป

d8k-a2m2 myvault  > mkdir pki
d8k-a2m2 myvault  > vault write ssh-signer/config/ca generate_signing_key=true
Key           Value
---           -----
public_key    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDBiNoa0NEql/Vm+WY9XTEeziKAGrUqFjy1HDbu9nWVyQyPMgHFRMLMI8SKUMNe7oFQivIyuwucJWtca8YXZTvN2m4NnTzM1vWNNHU7Bn+9DOM8NdKr34jFL2bFtDrn/fyByOGpBH5/Vv/tDobFOe+tuOKLiwmdKRT5BOM9tuLT6lAbpYeDaQIpbLD2WDjYkMAX18Hg+RN/hjHXBJDXgu/Nh5I186zAzvasV1H84kjECm3SpmxunAc+pCZ52SI91KNvsdSiGkdKpllior0p+3dc92tU21EI6XAMRdoM6eqhBKIpCF8msnWSs49g2q+1PC0L+SArzxjRCya/uj+y4w42GagteBv/9QOWlkN5q8DTcj4DWkR2hSgL0ck2swAd6XDbJPH2iUVyEuCd9XwEtqENJ+x06JvZRRmAWe7mEZaCXIol2usmA1EAby+IbIsTkhkxHY3RooqGFBvJzMS73WFmzEqxVGb4n0NA8CeJRC9yu/jocSEFrJcLJbl5btbCTuv/rSnMitpCRZ3x+6uXNq/q+W6f+d14RDH3HzO68Fcn/6ggSjAjNbRNC/u+oRnzZClVjZPN0tXm5K2dnbmUHOwnjb0Vex89KeML5Wb3Qz9OADoxaz8GN4WK/vkCWxcEmFVDmvo/Swc/oGJSz5LisDVyo6TPc6e4BplSDwNJnYBimw==

d8k-a2m2 myvault  > vault read -field=public_key ssh-signer/config/ca > pki/trusted-user-ca-keys.pem

d8k-a2m2 myvault  > ls -la pki/trusted-user-ca-keys.pem
-rw-r--r--@ 1 drs  staff  725 Jan 29 00:42 pki/trusted-user-ca-keys.pem

#3 แก้ไข SSH Server Configuration

ในขั้นตอนนี้ต้อง remote access ไปยัง SSH Server ที่ต้องการ เพื่อให้ trust กับ Public Key ของ CA โดนจำเป็นต้องใช้สิทธิ์ root ในการแก้ไข /etc/ssh/sshd_config และ restart sshd service พร้อมทั้งสร้าง usera และ userb ไว้สำหรับทดสอบ

d8k-a2m2 myvault  > rsync -av pki/trusted-user-ca-keys.pem root@servera.d8k.io:/etc/ssh/
d8k-a2m2 myvault  > rsync -av pki/trusted-user-ca-keys.pem root@serverb.d8k.io:/etc/ssh/

d8k-a2m2 myvault  > ssh root@servera.d8k.io -C 'echo "TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem" >> /etc/ssh/sshd_config'
d8k-a2m2 myvault  > ssh root@serverb.d8k.io -C 'echo "TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem" >> /etc/ssh/sshd_config'

d8k-a2m2 myvault  > ssh root@servera.d8k.io -C 'systemctl restart sshd'
d8k-a2m2 myvault  > ssh root@serverb.d8k.io -C 'systemctl restart sshd'


d8k-a2m2 myvault  > ssh root@servera.d8k.io -C 'useradd -m -s /bin/bash usera;useradd -m -s /bin/bash userb'
d8k-a2m2 myvault  > ssh root@serverb.d8k.io -C 'useradd -m -s /bin/bash usera;useradd -m -s /bin/bash userb'

#4 สร้าง role ของ SSH Secrets Engine

role ของ SSH Secrets Engine เป็นการกำหนดคุณสมบัติของการอนุญาตที่ในการเชื่อมต่อไปยัง SSH Server ที่จะ signed ให้กับ public key ของ user เพื่อระบุขอกำหนดในการเชื่อมต่อ มีบางคุณสมบัติที่น่าสนใจดังนี้

  • allowed_users เป็นรายการรายชื่อผู้ใช้ ที่กำหนดให้สามารถอนุญาตให้เชื่อมต่อกับ SSH Server ได้ ถ้าไม่มีการระบุอยู่ในค่านี้ จะไม่สามารถอนุญาตได้
  • default_user ถ้าไม่ระบุผู้ใช้ในขั้นตอนการ signed จะเป็นการอนุญาตให้ผู้ใช้ที่ระบุในค่านี้ เชื่อมต่อกับ SSH Server
  • allowed_extensions คุณสมบัติในการเชื่อมต่อว่าสามารถใช้ความสามารถของ SSH Server รูปแบบใดได้เช่น
    • permit-x11-forwarding อนุญาตให้ใช้ X11 forwarding ได้เพื่อแสดงผลที่ฝั่ง client เมื่อเรียกใช้งาน X11 โปรแกรมที่ฝั่ง SSH Server
    • permit-agent-forwarding อนุญาตให้ใช้ Agent forwarding เมื่อเรียกใช้ ssh -A ทำให้สามารถใช้งาน SSH agent key ที่เก็บอยู่ที่ SSH Client และมีการเรียกใช้งาน SSH agenet เมื่อเชื่อมต่อไปยัง SSH Serer ปลายทางแล้ว
    • permit-port-forwarding อนุญาตให้ใช้ความสามารถ port forwardings จากเครื่อง SSH Cleint ไปยัง SSH Server เรื่องเรียกใช้ ssh -L หรือจาก SSH Server ไปยัง SSH Cleint เรื่องเรียกใช้ ssh -R
    • permit-pty คุณสมบัตินี้สำคัญมาก คือความสามาถที่ให้ผู้ใช้สามารถ ติดต่อสื่อสารกับ SSH Server ได้ผ่านทาง Pseudoterminal ทำให้ส่งคำสั่งต่าง ๆ ผ่าน shell ได้
    • permit-user-rc อนุญาตให้ RC file ที่เก็บไว้ที่ ~/.ssh/rc ทำงาน

      ข้อมูลคุณสมบัติที่สามาถกำหนดได้

  • default_extensions หากไม่ระบุคุณสมบัติในขั้นได้การ signed จะได้รับคุณสมบัติในการเชื่อมต่อตามที่ระบุไว้ในหัวข้อนี้
  • key_type กำหนดชนิดของ credentials ที่จะสร้างด้วย role นี้ ในกรณีที่เป็น SSH Singed Certificate ให้ระบุค่าเป็น ca
  • ttl กำหนดให้ใช้ key pair นี้ในการเข้าถึง SSH Server ได้ระยะเวลานานเท่าไหร่ ถ้าหมดระยะเวลาที่กำหนดไว้ ก็จะไม่สามารถใช้ key pair เข้าถึง SSH Server ได้อีก
  • allow_user_certificates เป็นระบุว่า role นี้สามารถ signed ให้ certificate ให้ผู้ใช้งานเพื่อใช้ login ได้

ในตัวอย่าง จะสร้าง role ชื่อว่า projecta-role สามารถ signed ให้ certificate ให้ผู้ใช้งานเพื่อใช้ login ได้ สามารถ singed ให้ login ให้ผู้ใช้ usera และ userb ได้ แต่ถ้าไม่ระบุรายชื่อขณะ singed ก็ให้ใช้ได้กับ usera และอนุญาตให้เชื่อมต่อแล้วสร้าง Pseudoterminal และใช้ความสามารถ port forwardings ได้ แต่ถ้าไม่ระบุก็อนุญาตแต่การสร้าง Pseudoterminal ได้อย่างเดียว สุดท้าย SSH Singed Certificate ที่สร้างจาก role นี้ สามารถใช้งานได้ 30 นาที

d8k-a2m2 myvault  > vault write ssh-signer/roles/projecta-role - <<EOF
{
    "algorithm_signer": "rsa-sha2-256",
    "allow_user_certificates": true,
    "allowed_users": "usera,userb",
    "allowed_extensions": "permit-pty,permit-port-forwarding",
    "default_extensions": {
        "permit-pty": ""
    },
    "key_type": "ca",
    "default_user": "usera",
    "ttl": "30m0s"
    }
EOF
Success! Data written to: ssh-signer/roles/projecta-role

ข้อมูลเพิ่มเติม SSH Secrets Enginer API: Create Role

#5 สร้าง key pair สำหรับผู้ใช้งาน และ signed ด้วย projecta-role

d8k-a2m2 myvault  > ssh-keygen -t ed25519 -N "" -C "user1" -f pki/user1
Generating public/private ed25519 key pair.
Your identification has been saved in pki/user1
Your public key has been saved in pki/user1.pub
The key fingerprint is:
SHA256:m57ypyyYtcnwAdKscqqlgX2e9FZnPTZGYlWDzqb6pqE user1
The key's randomart image is:
+--[ED25519 256]--+
|             oo  |
|            o  . |
|   o       +     |
|  . +     o =    |
|   o .  S. *     |
|o.o . o .o+ *    |
|o+o oB =++ o o   |
|.+ +ooO+.oo      |
|+   o.E+B*.      |
+----[SHA256]-----+

d8k-a2m2 myvault  > vault write -field=signed_key ssh-signer/sign/projecta-role \
                    public_key=@pki/user1.pub  > pki/user1-cert.pub

d8k-a2m2 myvault  > ssh-keygen -Lf pki/user1-cert.pub
pki/user1-cert.pub:
        Type: ssh-ed25519-cert-v01@openssh.com user certificate
        Public key: ED25519-CERT SHA256:m57ypyyYtcnwAdKscqqlgX2e9FZnPTZGYlWDzqb6pqE
        Signing CA: RSA SHA256:c5ibYGOLGKx3JzAk4Bdysv5niKhlwq5AwjNRMnaI5jM (using rsa-sha2-256)
        Key ID: "vault-root-9b9ef2a72c98b5c9f001d2ac72aaa5817d9ef456673d3646625583cea6faa6a1"
        Serial: 12370256149329507790
        Valid: from 2023-02-02T16:27:30 to 2023-02-02T16:58:00
        Principals:
                usera
        Critical Options: (none)
        Extensions:
                permit-pty

#6 ทดสอบ login

ในการทดสอบ จะทดสอบ 3 กรณี

  • ทดสอบ login ไปยัง servera.d8k.io และ serverb.d8k.io ด้วย usera
d8k-a2m2 myvault  > ssh -i pki/user1 -i pki/user1-cert.pub  \
                   usera@servera.d8k.io
Warning: Permanently added 'servera.d8k.io' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-50-generic x86_64)
[... truncated output ...]
usera@servera:~$ hostname
servera
usera@servera:~$ logout
Connection to servera.d8k.io closed.

d8k-a2m2 myvault  > ssh -i pki/user1 -i pki/user1-cert.pub \
                    usera@serverb.d8k.io
Warning: Permanently added 'serverb.d8k.io' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-50-generic x86_64)
[... truncated output ...]
usera@serverb:~$ hostname
serverb

พบว่า public key ที่ทำการ signed ด้วย projecta-role สามาถเข้าใช้งานที่ servera และ serverb ด้วย usera ได้

  • ทดสอบ login ไปยัง servera.d8k.io และ serverb.d8k.io ด้วย userb
d8k-a2m2 myvault  > ssh -i pki/user1 -i pki/user1-cert.pub \
                    userb@servera.d8k.io
Warning: Permanently added 'servera.d8k.io' (ED25519) to the list of known hosts.
userb@servera.d8k.io: Permission denied (publickey).

d8k-a2m2 myvault  > ssh -i pki/user1 -i pki/user1-cert.pub \
                    userb@serverb.d8k.io
Warning: Permanently added 'serverb.d8k.io' (ED25519) to the list of known hosts.
userb@serverb.d8k.io: Permission denied (publickey).

พบว่า public key ที่ทำการ signed ด้วย projecta-role ไม่สามาถเข้าใช้งานที่ servera และ serverb ด้วย userb ได้

  • ทดสอบ login ไปยัง servera.d8k.io ด้วย usera และร้องขอเพื่อทำ port forwarding
d8k-a2m2 myvault  > ssh -i pki/user1 -i pki/user1-cert.pub \
                    -L 8080:serverb.d8k.io:80 \
                    usera@servera.d8k.io
Warning: Permanently added 'servera.d8k.io' (ED25519) to the list of known hosts.
usera@servera.d8k.io: Permission denied (publickey).

พบว่า public key ที่ทำการ signed ด้วย projecta-role ไม่สามาถเข้าใช้งานที่ servera ถ้าระบุให้ร้องขอเพื่อทำ port forwarding

#7 ทดสอบการปรับแต่งค่าต่าง ๆ ในขั้นตอน signed

จากข้อ 6 เป็นการ singed โดยใช้ค่า default ต่าง ๆ ของ role ที่กำหนดไว้ตั้งแต่สร้าง role ในหัวข้อนี้ จะปรับเปลี่ยน โดยจะ signed เพื่อใช้ใช้งานด้วย userb และทำการ port forwarding และ อนุญาตให้ใช้ login ได้เพียง 5 นาที

note ผู้อ่านที่ไม่คุ้นชินกับ heredoc และ shell expension อาจจะกับคำสั่งนิดหน่อยนะครับ แต่โดยหลักการก็คือผมต้องการให้ ผลการทำงานของคำสั่ง vault เขียนลงที่ไฟล์ pki/user1-cert.pub และ รับ argument แบบ multiple line ได้ ด้วยคำสั่งเพียงครั้งเดียว

d8k-a2m2 myvault  > vault >pki/user1-cert.pub write -field=signed_key ssh-signer/sign/projecta-role -   <<EOF
{
  "public_key": "$(cat ./pki/user1.pub)",
  "valid_principals": "userb",
  "extensions": {
      "permit-pty": "",
      "permit-port-forwarding": ""
   },
  "ttl": "5m0s"
}
EOF

d8k-a2m2 myvault  > ssh-keygen -Lf pki/user1-cert.pub
pki/user1-cert.pub:
        Type: ssh-ed25519-cert-v01@openssh.com user certificate
        Public key: ED25519-CERT SHA256:m57ypyyYtcnwAdKscqqlgX2e9FZnPTZGYlWDzqb6pqE
        Signing CA: RSA SHA256:c5ibYGOLGKx3JzAk4Bdysv5niKhlwq5AwjNRMnaI5jM (using rsa-sha2-256)
        Key ID: "vault-root-9b9ef2a72c98b5c9f001d2ac72aaa5817d9ef456673d3646625583cea6faa6a1"
        Serial: 12839503341863845183
        Valid: from 2023-02-02T17:57:17 to 2023-02-02T18:02:47
        Principals:
                userb
        Critical Options: (none)
        Extensions:
                permit-port-forwarding
                permit-pty

#8 ทดสอบ login พร้องทั้ง port forwarding

ทดสอบ login ไปยัง servera.d8k.io ด้วย userb และร้องขอให้ทำการ port forwarding ไปยัง serverb.d8k.io port 80

[terminal #1]

d8k-a2m2 myvault  > ssh -i pki/user1 -i pki/user1-cert.pub \
                    -L 8080:serverb.d8k.io:80 \
                    userb@servera.d8k.io

Warning: Permanently added 'servera.d8k.io' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-50-generic x86_64)
[... truncated output ...]
userb@servera:~$

[terminal #2]

d8k-a2m2 myvault  > curl http://serverb.d8k.io
Welcome to serverb.d8k.io!
d8k-a2m2 myvault  > curl http://localhost:8080
Welcome to serverb.d8k.io!

พบว่า สามารถเชื่อมต่อไปยัง servera.d8k.io ด้วย userb ได้ และทดสอบการเชื่อมต่อไปยัง localhost:8080 จะได้ข้อมูลเดียวกับ การเชื่อมต่อไปยัง serverb.d8k.io:80

ข้อสังเกตุ

ในตอนนี้ เราจะเป็นภาพในการเลือกใช้งาน SSH Singed Certificate โดยให้ HashiCorp Vault ทำหน้าที่เป็น Certificate Authority ให้ และ ทำหน้าที่ siged ให้ด้วย แต่ว่า user ที่ทำงานกับ HashiCorp Vault ยังเป็น Root Token อยู่ ไม่เหมาะสำหรับการทำงานงานจริง และอีกปัญหาหนึ่งก็คือ การ signed เพียงครั้งเดียว จะทำให้ใช้งาน login ไปยัง server ทำตัวที่ Trusted กับ CA ตัวนั้นได้ทุก server เลย ซึีงในบางสถานการณ์อาจจะไม่ได้ต้องการให้ใช้งานกัน SSH Server ทุกตัว … อ่านตอนในตอนที่ 3 ครับ

Made by มูลค่าความสุข

Share on

Tags

Human knowledge belongs to the world

a line from the movie "Antitrust"