macOS Internals Series · Vol. 4

SYSTEM SECURITY KEYCHAIN · SSH · GPG · CERTS

GUI-то крие 90% от security инструментите на macOS. Keychain от терминала, SSH config като pro, GPG подписване, TLS cert инспекция — всичко с команди.

4
теми
38
команди
0
GUI нужни
параноя
01 · keychain

KEYCHAIN от терминала

Keychain Access GUI е само frontend. Реалната сила е в security командата — пълен CRUD на пароли, сертификати и ключове.

Как работи macOS Keychain

Keychain е encrypted database (SQLite + AES-256) в ~/Library/Keychains/. Има три вида items: generic passwords (app credentials), internet passwords (browser/URLs) и certificates/keys. security командата е официалният CLI за всичко.

намери парола в Keychain
# Намери internet password (браузър стил)
$ security find-internet-password -s "github.com" -w

# Намери generic password по service name
$ security find-generic-password -s "MyApp" -w

# Пълна информация за item-а (без паролата)
$ security find-generic-password -s "MyApp"
-w флагът = print only password. Без него получаваш целия keychain item с metadata. Комбинирай: security find-internet-password -s "github.com" -w | pbcopy — паролата директно в клипборда.
добавяне и изтриване
# Добави generic password
$ security add-generic-password \
    -s "MyService" \
    -a "myusername" \
    -w "supersecretpassword" \
    -T ""            # -T "" = само текущия процес има достъп

# Добави internet password
$ security add-internet-password \
    -s "api.example.com" \
    -a "apiuser" \
    -w "mytoken123"

# Изтрий
$ security delete-generic-password -s "MyService"
Практически use case: В shell скриптове никога не hardcode-вай credentials. Вместо API_KEY="abc123"API_KEY=$(security find-generic-password -s "MyAPI" -w). Паролата остава в Keychain, скриптът е чист.
листване и одит на Keychain
# Листни всички Keychain-и в системата
$ security list-keychains

# Dump всички items (без пароли) — за одит
$ security dump-keychain | grep "svce\|acct\|srvr"

# Провери кои apps имат достъп до конкретен item
$ security dump-keychain -d | grep -A 5 "github"

# Създай нов keychain (за CI/CD изолация)
$ security create-keychain -p "keychainpassword" ci-build.keychain
$ security unlock-keychain -p "keychainpassword" ci-build.keychain
CI/CD trick: Отделен keychain за build процесите — изолиран от system keychain, автоматично заключва след сесията, може да се изтрие след deploy. Стандарт при Xcode cloud builds.
02 · ssh config

SSH Config — отвъд basics

~/.ssh/config е най-подценяваният файл на sysadmin-а. Правилно настроен — спестява часове всяка седмица.

~/.ssh/config — пълен pro setup
# ═══════════════════════════════════════
# ~/.ssh/config — Pro Setup
# ═══════════════════════════════════════

# ── Глобални defaults за всички хостове
Host *
    ServerAliveInterval     60     # keepalive всеки 60s
    ServerAliveCountMax     3      # 3 пъти, после disconnect
    AddKeysToAgent          yes    # автоматично в ssh-agent
    UseKeychain             yes    # macOS Keychain интеграция
    IdentityFile            ~/.ssh/id_ed25519
    Compression             yes    # компресия за бавни връзки
    ControlMaster           auto   # мултиплексиране —
    ControlPath             ~/.ssh/cm_%r@%h:%p
    ControlPersist          10m    # # пази конекцията 10 мин

# ── Production сървър
Host prod
    HostName                203.0.113.10
    User                    deploy
    IdentityFile            ~/.ssh/prod_ed25519
    Port                    2222
    ForwardAgent            no     # НИКОГА на prod!

# ── Dev сървър с jump host
Host dev-internal
    HostName                10.0.0.50
    User                    devuser
    ProxyJump               bastion.example.com

# ── Bastion / Jump сървър
Host bastion.example.com
    User                    jumpuser
    IdentityFile            ~/.ssh/bastion_ed25519
    ForwardAgent            yes    # само на bastion е ОК

# ── GitHub — без нужда от паролa
Host github.com
    User                    git
    IdentityFile            ~/.ssh/github_ed25519
    IdentitiesOnly          yes    # само този ключ

# ── Wildcard за цяла subnet
Host 10.0.1.*
    User                    admin
    StrictHostKeyChecking   no     # за dev мрежи
    UserKnownHostsFile      /dev/null
ControlMaster + ControlPersist е най-игнорираната оптимизация. Втората и третата SSH конекция към същия хост не минават през TCP handshake — ползват съществуващия socket. git push след git pull = моментално.
Опция Стойност Защо е важна
UseKeychainyesSSH passphrase се пази в macOS Keychain — не я въвеждаш при всяка конекция
AddKeysToAgentyesКлючът се добавя в ssh-agent автоматично при първа употреба
ForwardAgentno (prod) / yes (bastion)Никога не forward-вай agent към production — компрометиран сървър може да го ползва
ProxyJumpbastion hostЗамества ProxyCommand с по-прост синтаксис. SSH през jump host прозрачно.
IdentitiesOnlyyesБез него ssh опитва всички ключове — може да trigger-не lockout при много опити
ControlPersist10mМултиплексирана конекция живее 10 минути след затваряне — последващите са instant
SSH ключове — генерирай правилно
# Ed25519 — съвременен, бърз, малък ключ (препоръчан)
$ ssh-keygen -t ed25519 -C "work-laptop-2024" -f ~/.ssh/github_ed25519

# RSA 4096 ако сървърът не поддържа Ed25519
$ ssh-keygen -t rsa -b 4096 -C "legacy-server-key"

# Провери fingerprint на ключ
$ ssh-keygen -lf ~/.ssh/id_ed25519.pub
256 SHA256:abc123XYZ... work-laptop-2024 (ED25519)

# Провери всички ключове в ssh-agent
$ ssh-add -l

# Добави ключ в macOS Keychain (запомня passphrase)
$ ssh-add --apple-use-keychain ~/.ssh/id_ed25519

# Тествай конекция с verbose за дебъгване
$ ssh -vvv git@github.com 2>&1 | grep "Offering\|Authenticated"
Никога RSA 2048 или по-малко — счупен е за практически цели. Ed25519 е стандартът от 2020+. По-малки ключове, по-бързи операции, по-силна математика.
03 · gpg

GPG — подписвай и криптирай

GPG на macOS с git commit подписване, файл криптиране и правилен agent setup. Изисква: brew install gnupg pinentry-mac

⚠ Prerequisite
brew install gnupg pinentry-mac — след инсталация: добави pinentry-program /opt/homebrew/bin/pinentry-mac в ~/.gnupg/gpg-agent.conf. Без pinentry-mac ще получаваш грешки при всяка операция.
GPG ключове — генериране и управление
# Генерирай нов GPG ключ (интерактивно)
$ gpg --full-generate-key
# → Избери: RSA 4096 или Ed25519, 2-3 години валидност

# Листни всички ключове
$ gpg --list-keys --keyid-format LONG
pub   ed25519/3AA5C34371567BD2 2024-01-15 [SC] [expires: 2026-01-15]
      4B2C4A1F5E8D9F2A1B3C4D5E6F7G8H9I0J1K2L3M
uid   [ultimate] Alex Admin <alex@example.com>
sub   cv25519/1B2C3D4E5F6G7H8I 2024-01-15 [E]

# Листни private ключове
$ gpg --list-secret-keys --keyid-format LONG

# Експортирай публичен ключ
$ gpg --armor --export alex@example.com | pbcopy

# Fingerprint на ключ
$ gpg --fingerprint alex@example.com
Git commit подписване с GPG
# Настрой git да ползва GPG ключа
$ git config --global user.signingkey 3AA5C34371567BD2
$ git config --global commit.gpgsign true
$ git config --global gpg.program gpg

# Кажи на GPG agent да ползва правилния TTY
$ echo 'export GPG_TTY=$(tty)' >> ~/.zshrc

# Подпиши commit ръчно (ако нямаш auto-sign)
$ git commit -S -m "feat: add authentication"

# Провери подписа на commit
$ git log --show-signature -1
gpg: Signature made Wed Jan 15 14:23:11 2024
gpg: Good signature from "Alex Admin <alex@example.com>"
commit abc123def456...
GitHub показва "Verified" badge на commit-ите ти след като добавиш GPG публичния ключ в Settings → SSH and GPG keys. Доказва, че commit-ът наистина е от теб — не може да се фалшифицира.
GPG криптиране на файлове
# Криптирай файл за конкретен получател
$ gpg --encrypt --recipient colleague@example.com --armor secrets.txt
# → secrets.txt.asc

# Криптирай само за себе си (backup на sensitive файлове)
$ gpg --encrypt --recipient alex@example.com credentials.csv

# Декриптирай
$ gpg --decrypt secrets.txt.gpg > secrets.txt

# Подпиши файл (доказва автентичност, не криптира)
$ gpg --armor --detach-sign release-v1.0.tar.gz
# → release-v1.0.tar.gz.asc

# Верифицирай подпис
$ gpg --verify release-v1.0.tar.gz.asc release-v1.0.tar.gz
gpg: Good signature from "Alex Admin <alex@example.com>"
Symmetric криптиране без ключове (с парола): gpg --symmetric --armor file.txt → пита за парола, криптира. Подходящо за backup на sensitive файлове в облака.
GPG agent config + troubleshoot
# ~/.gnupg/gpg-agent.conf
pinentry-program /opt/homebrew/bin/pinentry-mac
default-cache-ttl 3600      # кеширай passphrase 1 час
max-cache-ttl    86400     # максимум 24 часа
enable-ssh-support         # GPG agent като SSH agent

# Рестартирай agent след промяна на конфига
$ gpgconf --kill gpg-agent
$ gpg-agent --daemon

# Провери дали agent работи
$ gpgconf --list-dirs agent-socket
04 · tls certs

CERT инспекция

openssl и security командата за инспекция на TLS сертификати — remote и локално. Незаменими при debugging на HTTPS проблеми.

Subject:CN=example.com, O=Example Inc
Issuer:Let's Encrypt Authority X3
Valid From:2024-01-15 00:00:00
Valid Until:2024-04-15 23:59:59 ⚠ 47 days
SANs:example.com, www.example.com, api.example.com
Key:RSA 2048 bits
Signature:SHA256withRSA
OCSP:http://ocsp.letsencrypt.org
remote TLS cert инспекция
# Пълна cert информация за домейн
$ echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
    | openssl x509 -noout -text

# Само дата на изтичане
$ echo | openssl s_client -connect example.com:443 2>/dev/null \
    | openssl x509 -noout -dates
notBefore=Jan 15 00:00:00 2024 GMT
notAfter=Apr 15 23:59:59 2024 GMT

# Subject Alternative Names (всички домейни в cert-а)
$ echo | openssl s_client -connect example.com:443 2>/dev/null \
    | openssl x509 -noout -ext subjectAltName

# Дни до изтичане (за мониторинг)
$ echo | openssl s_client -connect example.com:443 2>/dev/null \
    | openssl x509 -noout -checkend 2592000  # 30 дни = 2592000 секунди
Certificate will not expire   # или:
Certificate will expire        # → alert!
Последната команда е идеална за cron job за мониторинг на cert expiry: echo | openssl s_client -connect site.com:443 2>/dev/null | openssl x509 -noout -checkend 2592000 || say "Certificate expiring soon!"
cert chain и TLS версия
# Пълна cert chain
$ openssl s_client -connect example.com:443 -showcerts 2>/dev/null \
    | grep "s:\|i:"
 s:CN = example.com
 i:CN = Let's Encrypt R3
 s:CN = Let's Encrypt R3
 i:CN = ISRG Root X1

# Тествай конкретна TLS версия
$ openssl s_client -connect example.com:443 -tls1_2 2>&1 | grep "Protocol"
$ openssl s_client -connect example.com:443 -tls1_3 2>&1 | grep "Protocol"

# Провери cipher suite
$ openssl s_client -connect example.com:443 2>/dev/null \
    | grep "Cipher is"
    Cipher is TLS_AES_256_GCM_SHA384

# Инспектирай локален .pem / .crt файл
$ openssl x509 -in certificate.pem -noout -text
$ openssl x509 -in certificate.crt -noout -subject -issuer -dates
self-signed cert за local dev
# Генерирай self-signed cert за localhost (10 години)
$ openssl req -x509 -newkey rsa:4096 -keyout localhost.key \
    -out localhost.crt -days 3650 -nodes \
    -subj "/CN=localhost" \
    -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

# Добави в macOS Keychain и trust-вай
$ security add-trusted-cert -d -r trustRoot \
    -k /Library/Keychains/System.keychain localhost.crt

# Провери дали е trusted
$ security verify-cert -c localhost.crt -p ssl
...certificate verification successful.
-addext "subjectAltName" е задължителен от Chrome 58+. Без SAN браузърът показва "Not Secure" дори за valid self-signed cert. -nodes = no DES = без парола на private ключа (удобно за dev сървъри).
05 · security одит

Security ОДИТ

Бързи команди за проверка на security статуса на macOS системата.

macOS security checklist — едно по едно
# FileVault статус (disk encryption)
$ fdesetup status
FileVault is On.

# SIP статус
$ csrutil status
System Integrity Protection status: enabled.

# Firewall статус
$ /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
Firewall is enabled. (State = 1)

# Gatekeeper
$ spctl --status
assessments enabled

# Провери всички sudoers
$ cat /etc/sudoers
$ ls -la /etc/sudoers.d/

# Потребители с admin права
$ dscl . -read /Groups/admin GroupMembership
GroupMembership: root alex

# Намери SUID/SGID binaries (потенциален exploit вектор)
$ sudo find / -perm -4000 -type f 2>/dev/null | head -20
провери какво се стартира автоматично
# Launch Agents за текущия потребител
$ ls -la ~/Library/LaunchAgents/
$ ls -la /Library/LaunchAgents/
$ ls -la /Library/LaunchDaemons/

# Всички заредени launch jobs
$ launchctl list | grep -v "com.apple"  # само third-party

# Login items (Settings → General → Login Items)
$ sfltool dumpbtm 2>/dev/null | grep "url\|name"

# Намери persistence механизми (malware hunting)
$ find ~/Library/LaunchAgents /Library/Launch{Agents,Daemons} \
    -name "*.plist" -newer /tmp -ls 2>/dev/null
Последната команда намира наскоро добавени plist файлове в launch директориите — класически признак за malware persistence. Легитимните Apple services не се променят след OS инсталация.
network security одит
# Всички LISTEN портове с процес
$ sudo lsof -iTCP -sTCP:LISTEN -n -P

# Провери за outbound конекции към непознати IP-та
$ netstat -an | grep ESTABLISHED | awk '{print $5}' \
    | cut -d: -f1 | sort -u

# Resolve IP-тата към домейни
$ netstat -an | grep ESTABLISHED | awk '{print $5}' \
    | cut -d: -f1 | sort -u \
    | xargs -I{} sh -c 'echo -n "{} → "; host {} 2>/dev/null | grep "domain name" | head -1'

# Провери /etc/hosts за манипулация
$ cat /etc/hosts | grep -v "^#\|^$\|localhost\|broadcasthost"
Последната команда показва всички нестандартни hosts записи — malware и adware обичат да redirect-ват домейни тук. Ако видиш непознати записи — investigate.
💡 Security Automation
Сложи всички audit команди в скрипт ~/bin/security-check и го пускай веднъж седмично: chmod +x ~/bin/security-check && crontab -e0 9 * * 1 ~/bin/security-check | mail -s "Weekly Security Check" you@example.com