寫在前邊html
這一篇文章是基於 Gitea+Drone CI+Vault 打造屬於本身的CI/CD工做流系列文章第二篇,讓咱們一塊兒來完成 drone
與 vault
的搭配使用,這篇主要講 vault
的部署和使用,以及怎麼經過 drone
來使用 vault
node
上一篇文章(一) Drone CI For Github —— 打造本身的CI/CD工做流咱們一塊兒瞭解了,Drone
的部署和使用,一塊兒感覺了 Drone
的簡單強大的功能帶來的方便和快捷。在實際的應用中,咱們會有不少的敏感數據,Drone
自身的 Secret
是以倉庫爲單位進行管理,並且也沒有嚴格獨立的權限控制,實際上是不太方便的。mysql
帶着這樣的疑惑,我再次向@Dee luo老哥尋求幫助,老哥掏了掏肚子上的口袋,取出一本武林祕籍,曰:「小夥子,我這裏有一個色情網站記錄在上面,你看看」。我打開只看到 www.vaultproject.io 一行小字赫然記錄在內,我火燒眉毛的打開,美滋滋的看了起來。nginx
此處省略一萬個字...git
另外強烈推薦YUHAO的博客 私密信息管理利器 HashiCorp Vault系列文章github
好了,如今我已經知道該作什麼了。web
說幹就幹,開始搞事。sql
Vault是一個管理Secrets並保護敏感數據的工具,來自HashiCorp,若是你對這個名字有點陌生,那麼你必定知道Vagrantdocker
Vault是一種安全訪問
Secret
的工具。Secret
就是您要嚴格控制訪問的任何內容,例如API密鑰,密碼或證書。Vault爲任何機密提供統一的界面,同時提供嚴格的訪問控制並記錄詳細的審計日誌。數據庫現代系統須要訪問大量
Secret
:數據庫憑證,外部服務的API密鑰,面向服務的體系結構通訊的憑證等。瞭解誰正在訪問哪些祕密已經很是困難且特定於平臺。若是沒有自定義解決方案,幾乎不可能添加密鑰滾動,安全存儲和詳細的審計日誌。這是Vault介入的地方。Vault的主要功能包括:
- 安全祕密存儲:任意密鑰/值祕密能夠存儲在Vault中。Vault會在將這些機密寫入持久存儲以前加密這些機密,所以獲取對原始存儲的訪問權限不足以訪問您的機密。Vault能夠寫入磁盤,Consul等。
- 動態祕密:Vault能夠按需爲某些系統生成機密,例如AWS或SQL數據庫。例如,當應用程序須要訪問S3存儲桶時,它會要求Vault提供憑據,Vault將根據須要生成具備有效權限的AWS密鑰對。建立這些動態機密後,Vault也會在租約到期後自動撤消它們。
- 數據加密:Vault能夠加密和解密數據而無需存儲數據。這容許安全團隊定義加密參數,並容許開發人員將加密數據存儲在SQL等位置,而無需設計本身的加密方法。
- 租賃和續訂:Vault中的全部機密都有與之相關的租約。在租約結束時,Vault將自動撤銷該祕密。客戶能夠經過內置續訂API續訂租約。
- 撤銷:Vault內置了對祕密撤銷的支持。保險櫃不只能夠撤銷單個祕密,還能夠撤銷祕密樹,例如特定用戶讀取的全部祕密,或特定類型的全部祕密。撤銷有助於關鍵滾動以及在入侵狀況下鎖定系統。
上邊都是廢話,這裏說一下我本身的體驗。
首先我沒有體驗的很深,我如今只是體驗了 key/value
結構和 database
,而後看了一下 ACL Policies
管理權限。
vault
這裏並非單獨部署vault,而是參考上一篇文章來結合vault使用,在上一篇文章的
docker-compose.yml
基礎上,加入vault
的容器,併爲vault
提供web
服務.
爲了區分,如下代碼塊中,用
$
開頭表示宿主機命令,用/ #
開頭表示容器中的命令
...
vault:
image: vault:latest
container_name: vault
restart: always
networks:
- dronenet
volumes:
- ./vault/file:/vault/file
- ./vault/config:/vault/config
- ./vault/logs:/vault/logs
cap_add:
- IPC_LOCK
environment:
- VAULT_ADDR=http://127.0.0.1:8200
command: vault server -config=/vault/config/local.json #這句很是重要,必定要替換原有的Dockerfile中的CMD,否則會自動初始化,生成的數據都在docker logs中,不說你確定找不到。dog.jpg,因此我選擇手動初始化
...
複製代碼
vault
事先準備好一個數據庫和對該數據庫具備訪問權限的數據庫帳號
參考
database
: vaultname
: vaultpassword
: vault123456樣例中的配置請事先閱讀文檔知曉含義
參考Vault Configuration
Vault
的配置文件是HCL或者json
,這裏我使用json
HCL to json
請參考HCL
vault
$ mkdir -p vault/config
$ mkdir -p vault/file
$ mkdir -p vault/logs
$ vim vault/config/local.json
{
"ui": true,
"storage": {
"mysql": {
"address": "mysql:3306",
"username": "vault",
"password": "vault123456"
}
},
"listener": {
"tcp": {
"address": "0.0.0.0:8200",
"tls_disable": 1
}
},
"backend": {
"file": {
"path": "/vault/file"
}
},
"log_level": "Debug", #調試階段建議開啓Debug
"default_lease_ttl": "168h",
"max_lease_ttl": "720h"
}
$ docker-compose up -d
$ docker-compose exec vault ash
/ # vault operator init
Unseal Key 1: cz/cSHqUep5IBQjtDWBgFnN+G02hLFh8s/19rPxKjxCe #記下來
Unseal Key 2: o+BRjfy64sUKLTWKV0jV+JjvKZWd0R3ibBR7IUbCn8sB #記下來
Unseal Key 3: xiJF+XI8gF1PWGMvOYhy0go16x2VgZdAVKw/xBIVGeo7 #記下來
Unseal Key 4: xd/H1hBdPGwm2qchkShgzGbVtWWHeeCv8S1RyYg34yKi #記下來
Unseal Key 5: 4OOHfxxwuX7Hz40E/bHJLbkwLLWeZkWnz7/pmdtgm7mn #記下來
Initial Root Token: s.RIeC53WBWizfl0OXVbDYuxbh #記下來
Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.
Vault does not store the generated master key. Without at least 3 key to
reconstruct the master key, Vault will remain permanently sealed!
It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.
複製代碼
初始化動做爲咱們生成了 5 個 Unseal key,此外還有默認的 Root Token。因此應該立刻把這些信息記錄到安全的地方,由於之後你是沒有辦法再看到它們的。
編輯完記得重啓nginx服務
server {
listen 80;
server_name vault.yiranzai.top;
location / {
proxy_pass http://vault:8200;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
複製代碼
vault.yiranzai.top
Vault 對於數據保護是很是重視的。服務器啓動後,並不可以立刻訪問其數據,而必須通過一個解封(Unseal)的動做。
依次輸入上邊獲得的五個
Unseal Key
中的三個來解封服務,而後使用Root Token
登陸,登陸後如圖,右側會有教程,先跟着作一邊熟悉熟悉。
Secrets Engines
這個東西,怎麼說呢,不太好理解。字面意思就是
祕密引擎
,是存儲,生成或加密數據的一個組件,能夠理解爲一個數據集,也能夠理解爲一種數據渠道。只有Secrets Engines
存在,數據才能被指定方式存儲。
- Generic
- KV (這裏只示範這種)
- PKI Certificates
- SSH
- Transit
- TOTP
- Cloud
- Active Directory
- AliCloud
- AWS
- Azure
- Google Cloud
- Google Cloud KMS
- Infra
- Consul
- Databases (支持多種驅動)
- Nomad
- RabbitMQ
ACL Policy
建立一個
ACL Policy
,配置爲僅對剛剛建立的Secrets Engine
——dronetest
有可讀和列表權限。ACL Policy
的配置格式爲HCL,HCL 是 HashiCorp 創造的、專門用於配置文件的語言格式)
# Allow tokens to look up their own properties
path "auth/token/lookup-self" {
capabilities = ["read"]
}
# Allow tokens to renew themselves
path "auth/token/renew-self" {
capabilities = ["update"]
}
# Allow tokens to revoke themselves
path "auth/token/revoke-self" {
capabilities = ["update"]
}
# Allow a token to look up its own capabilities on a path
path "sys/capabilities-self" {
capabilities = ["update"]
}
path "dronetest" {
capabilities = ["read", "list"]
}
path "dronetest/*" {
capabilities = ["read", "list"]
}
複製代碼
至此,咱們的
Vault
就配置好了,接下來讓咱們建立secret
Secret
上面咱們簡單配置了一下
Vault
,如今讓咱們建立Secret
首先咱們明白的是
dronetest(Policy)
對dronetest(Secrets Engine)
只有可讀和列表的權限。
回到
vault
容器中,使用Root token
登陸
/ # vault login s.RIeC53WBWizfl0OXVbDYuxbh
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 s.RIeC53WBWizfl0OXVbDYuxbh
token_accessor 5JoYSxx4CqR10mmeG7HusChz
token_duration ∞
token_renewable false
token_policies ["root"]
identity_policies []
policies ["root"]
複製代碼
token
這個token應當只對
dronetest(Secrets Engine)
可讀,因此咱們爲他分配一個dronetest(Policy)
/ # vault token create -policy=dronetest -no-default-policy
Key Value
--- -----
token s.mx4KQycrFAfiaHIuPnNLhFCy #記下來很是重要
token_accessor 8rzot4pDJvfX0JTi2ImYLvg4
token_duration 168h
token_renewable true
token_policies ["dronetest"]
identity_policies []
policies ["dronetest"]
複製代碼
Secret
如圖所示,這裏咱們爲
web
建立了兩對KV
接下來嘗試用命令建立
/ # vault kv put dronetest/test v=k
Key Value
--- -----
created_time 2019-02-26T03:53:02.240676864Z
deletion_time n/a
destroyed false
version 1
/ # vault kv get dronetest/test
====== Metadata ======
Key Value
--- -----
created_time 2019-02-26T03:53:02.240676864Z
deletion_time n/a
destroyed false
version 1
== Data ==
Key Value
--- -----
v k
/ # vault kv put dronetest/test a=b
Key Value
--- -----
created_time 2019-02-26T03:53:31.073689586Z
deletion_time n/a
destroyed false
version 2
/ # vault kv get dronetest/test
====== Metadata ======
Key Value
--- -----
created_time 2019-02-26T03:53:31.073689586Z
deletion_time n/a
destroyed false
version 2
== Data ==
Key Value
--- -----
a b
/ # curl \
> --header "X-Vault-Token: s.RIeC53WBWizfl0OXVbDYuxbh" \
> http://127.0.0.1:8200/v1/dronetest/data/test
{"request_id":"f5ce7d8f-cc60-ec79-20a5-5875c5e4362c","lease_id":"","renewable":false,"lease_duration":0,"data":{"data":{"a":"b"},"metadata":{"created_time":"2019-02-26T03:53:31.073689586Z","deletion_time":"","destroyed":false,"version":2}},"wrap_info":null,"warnings":null,"auth":null}
複製代碼
能夠看出後者覆蓋了前者,注意
version
增長了1
,vault
爲每一個path
每次寫入的數據都定義爲了一個新的版本,這樣就不難理解,這裏爲何是後者覆蓋了前者。
Drone
中使用 Vault
以上咱們瞭解了
Vault
簡單的部署和使用,如今咱們一塊兒瞭解怎麼在Drone
中使用Vault
結合上一篇文章(一) Drone CI For Github —— 打造本身的CI/CD工做流,咱們將
Vault
加入進來而後咱們還須要
drone-vault
這個插件來實現Drone
和Vault
之間的通訊中轉參考
- Drone使用Vault存儲Secret (仔細看,文檔是有缺失的)
- Drone-vault源碼分析 (之因此要分析,是由於官方文檔中沒有說明怎麼和vault通訊,觀看源碼一目瞭然)
version: "3.7"
services:
nginx:
image: nginx:alpine
container_name: dronetest_nginx
ports:
- "80:80"
restart: always
networks:
- dronenet
mysql:
image: mysql:5.7
restart: always
container_name: dronetest_mysql
environment:
- MYSQL_ROOT_PASSWORD=root_password
- MYSQL_DATABASE=drone
- MYSQL_USER=drone
- MYSQL_PASSWORD=drone_password
networks:
- dronenet
volumes:
- /path/to/conf/my.cnf:/etc/mysql/my.cnf:rw
- /path/to/data:/var/lib/mysql/:rw
- /path/to/logs:/var/log/mysql/:rw
vault:
image: vault:latest
container_name: vault
restart: always
networks:
- dronenet
volumes:
- ./vault/file:/vault/file
- ./vault/config:/vault/config
- ./vault/logs:/vault/logs
cap_add:
- IPC_LOCK
environment:
- VAULT_ADDR=http://127.0.0.1:8200
command: vault server -config=/vault/config/local.json #這句很是重要,必定要替換原有的Dockerfile中的CMD,否則會自動初始化,生成的數據都在docker logs中,不說你確定找不到。dog.jpg,因此我選擇手動初始化
drone-server:
image: drone/drone:1.0.0-rc.5 #不要用latest,latest並不是穩定版本
container_name: dronetest_server
networks:
- dronenet
volumes:
- ${DRONE_DATA}:/var/lib/drone/:rw
- /var/run/docker.sock:/var/run/docker.sock:rw
restart: always
environment:
- DRONE_DEBUG=true
- DRONE_DATABASE_DATASOURCE=drone:drone_password@tcp(dronetest_mysql:3306)/drone?parseTime=true #mysql配置,要與上邊mysql容器中的配置一致
- DRONE_DATABASE_DRIVER=mysql
- DRONE_GITHUB_SERVER=https://github.com
- DRONE_GITHUB_CLIENT_ID=${Your-Github-Client-Id} #Github Client ID
- DRONE_GITHUB_CLIENT_SECRET=${Your-Github-Client-Secret} #Github Client Secret
- DRONE_RUNNER_CAPACITY=2
- DRONE_RPC_SECRET=YOU_KEY_ALQU2M0KdptXUdTPKcEw #RPC祕鑰
- DRONE_SERVER_PROTO=http #這個配置決定了你激活時倉庫中的webhook地址的proto
- DRONE_SERVER_HOST=dronetest.yiranzai.top
- DRONE_USER_CREATE=username:yiranzai,admin:true #管理員帳號,通常是你github用戶名
drone-vault:
image: drone/vault
container_name: dronetest_vault
restart: always
networks:
- dronenet
environment:
- SECRET_KEY=7890bcce69bb685a9a424767fe9d1be1 #和drone-agent通訊的加密
- DEBUG=true
- VAULT_ADDR=http://vault:8200
- VAULT_TOKEN_RENEWAL=84h
- VAULT_TOKEN_TTL=168h
- VAULT_TOKEN=s.mx4KQycrFAfiaHIuPnNLhFCy #這裏不要用root token,用上邊生成的只讀token
drone-agent:
image: drone/agent:1.0.0-rc.5
container_name: dronetest_agent
restart: always
networks:
- dronenet
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rw
environment:
- DRONE_SECRET_SECRET=7890bcce69bb685a9a424767fe9d1be1
- DRONE_SECRET_ENDPOINT=http://dronetest_vault:3000
- DRONE_RPC_SERVER=http://dronetest_server
- DRONE_RPC_SECRET=YOU_KEY_ALQU2M0KdptXUdTPKcEw
- DRONE_DEBUG=true
- DRONE_LOGS_DEBUG=true
- DRONE_LOGS_PRETTY=true
- DRONE_LOGS_NOCOLOR=false
networks:
dronenet:
複製代碼
.drone.yml
這裏的
.drone.yml
與上一篇文章中的相似,沒有太大改動。參考
dronetest
中的 web
建立一個新版本私鑰中的換行符須要替換成\n粘貼進來
{
"port":"22",
"name":"yiranzai",
"host":"dronetest.yiranzai.top",
"deploy_path":"/path/to/web",
"rsa":"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAu1uWokQH6tuExm1RcFTJj2F6b8TXUkAzpAywVFQuyIepQwVG\nfl+Y1iD8YrqB31ZDjJk3DbC6DYGsecJILpBKDJ6T1M4UPKF6m0DH+O4bb33rhBy/\nY/zeN2jOygKTfTWpxrs13ZLvsJzNsH7rs6P+K3c+2heAhrYlyzeXTO/VbqCAsjMp\nsDMRjCDEp7jxNeEOdGM/4gIxpatDkVSIbgCj1jkYJJ3C0ipS3KvI2A1Lic8Vjr4V\nxtRK82r4aa1qeWunUFJWaO1O/V11l63lixfrr12QDgRyAkC/GCVOso6+NRP+DfGj\nhGQ+XziBZRdaEkQwv4kI717l4ToFDvUj1G8K1QIDAQABAoIBADx0q/sIT0DYkpXb\nMPUw7W7361We6gBAXiPMTvot0pGeXeYSCiBpL/g40vwBvB47TzM8olcKDzqRAgKO\nn/IZXzNn2qLN4emT482p34b1O/AWtNFy7h4Le1SBernQULT1mQtIgt/rBMB2FzZ7\n//p4q0x3ZXKkRi+i/NTayi/pzW6DTxYreIek+FSIPPJ3Kc1tsooD8greiEYo3wef\nbzV17YIvEry3RRRKYcR/tjS/oWOKdG1YzxsiPVALrZtgHS3KcDCYRltQoALGfMjl\nL4iicfTe2Jlr36CvdH0yqFOsRPH0eh4dC1sFKBzHFqcccnNfnhOGgwCbj/zDEuX+\nDrUUnqECgYEA3NqvHSAg3ULF0Y+emRXDDLbLmBDsIX+7ygQeYcwC1EVVf7F2z+Tw\ntYTjTNu4HzXAHVQ3WcB4K0fXEAJuK0LtORpHPPxfMse6vURSOTubBqhp6+H33TTq\nJa0ph1vLvGp2G1sJHQ+QrwZw+SyJ2LtiSYlMTIf617DDYAc+iWoBc10CgYEA2XZ8\nfRy20YMb01znorEG7WQ/vErtasAJbN122M/e6a1FPMmDdiTuYvA4Q0xylDXBgwZ/\nXxBFc1fs6QI+Vks3J6lzAgJ+XbX3gtoPxTFRJu4yZjCmhxLOpr1zzR8eWWrJ3aA3\nIS2UmQhr8mGPPnQbc7OeLTUv+7ZokDElZmMpxekCgYB1ZPi4Lp/JfPjRz3mp3dt0\nIqZOCpC1rcAQPeg4a80FMGWmHprdHwCkPCLmc3SHIncgH+sMjjZSKzmyFNiivkyC\nkelUDYI813XnTS23pmtdOqAy3kmPD3V2eXkd0D6XxK3LEzTg8akin/XlPTt4rQIt\nvIGGHLHN/jOcE722JVboMQKBgQCFOyqaHHWFdyYdINZpvrvXxYum+ODsfitIH4co\n3nJcCGRbEbsRLx8+Tp6p3LR2SVj3xYVT4MwsFrp3J4C1re8R1ac4m/1/u3ShHqh6\nz/RAPb3zDGt6ZfNmBVk3WstlTR/e3QV+xkY8XASGw27XfJs1D37hI6z6Mo3tiC61\nxBdbwQKBgHMDXneApZOsByrm0fvrOdIeW247kkmO6jLrI6QI/mR70gjODKXApLbU\nAbyglSi/i6Ewp+Au3+2mMvHFGc8iRkh0pwEo+xMKqPUAZEnmCbo3mKSjnR/vE3Pa\nO1frNKNLFff6kMh0ufbO3YIixYHCNJO6j/k1GmkkECSTMMka1tig\n-----END RSA PRIVATE KEY-----"
}
複製代碼
.drone.yml
---
kind: pipeline
name: drone
workspace:
base: /app
path: git/drone
steps:
- name: build
image: node:alpine
volumes:
- name: webroot
path: /wwwroot
commands:
- /bin/sh bash.sh
environment:
host:
from_secret: host
port:
from_secret: port
abc: abctest
- name: deploy
image: appleboy/drone-scp
when:
status:
- success
settings:
host:
from_secret: host
port:
from_secret: port
key:
from_secret: rsa
username:
from_secret: username
target:
from_secret: deploy_path
source: ./*
volumes:
- name: webroot
host:
path: /opt
- name: cache
host:
path: /tmp/cache
trigger:
branch:
- master
event:
- push
---
kind: secret
external_data:
host:
path: dronetest/data/web
name: host
username:
path: dronetest/data/web
name: name
port:
path: dronetest/data/web
name: port
deploy_path:
path: dronetest/data/web
name: deploy_path
rsa:
path: dronetest/data/web
name: rsa
複製代碼
git add .;git commit -m 'init test 4';
git push origin master
複製代碼
Drone
的 ACTIVITY FEED
若是成功就會以下圖所示
去服務器檢查一下
看到
bash.sh
和.drone.yml
都被上傳到這裏(只是測試,不是真的讓你這麼幹)
$ pwd
/home/www
$ ll -a
total 40
drwx------ 4 www www 4096 Feb 20 04:23 .
drwxr-xr-x. 4 root root 4096 Feb 20 03:55 ..
-rw------- 1 www www 61 Feb 19 03:00 .bash_history
-rw-r--r-- 1 www www 18 Oct 30 13:07 .bash_logout
-rw-r--r-- 1 www www 193 Oct 30 13:07 .bash_profile
-rw-r--r-- 1 www www 231 Oct 30 13:07 .bashrc
-rw-r--r-- 1 www www 35 Feb 20 04:23 bash.sh
-rw-r--r-- 1 www www 812 Feb 20 04:23 .drone.yml
drwxr-xr-x 8 www www 4096 Feb 20 04:23 .git
drwxr-xr-x 2 www www 4096 Feb 19 02:40 .ssh
複製代碼
寫到這裏,就算是結束了。說實話,有了上一篇文章的的經驗和踩坑,我已經對於 Drone
的文檔至關失望了,沒想到後邊有更深的坑在等着我,當我瞭解到真相之後,氣得差點砸鍵盤。
本次總結
vault
官方提供的高級教程)drone-vault
的官方樣例中沒有說明怎麼與 vault
通訊,在我第一遍的操做中,我沒有細想這個問題,跳過了,結果不用多說,天然是失敗。後面發現問題一臉矇蔽,這玩意他不說我怎麼找呢?仍是@Dee luo老哥提醒我,我纔想起來去看看源碼,在源碼中找到了相關參數。(強烈吐槽!!!倉庫仍是隻讀的!!!)drone-vault
是用GO語言開發的,drone-vault
使用的http
包,不支持沒寫協議的 url
,例如 vault:8200
不能被識別,要寫成 http(s)://vault:8200
vault
中的 Secret
存儲路徑是 {Engine Name}/data/{Secret Name}
,這一點要牢記若是你對 vault
想有更深層次的瞭解,能夠看下YUHAO的博客 私密信息管理利器 HashiCorp Vault系列文章
好了,祝你們擼碼愉快,沉迷於BUG不能自拔。
不要砸鍵盤!