diff --git a/Core.tcl b/Core.tcl --- a/Core.tcl +++ b/Core.tcl @@ -253,10 +253,15 @@ sql disconnect sql2 disconnect } - sql connect $sql(host) $sql(user) $sql(pass) - sql2 connect $sql(host) $sql(user) $sql(pass) + + set sql_credentials [dict get [vault_get mysql] data] + + sql connect $sql(host) [dict get $sql_credentials username] [dict get $sql_credentials password] + sql2 connect $sql(host) [dict get $sql_credentials username] [dict get $sql_credentials password] sql selectdb $sql(database) sql2 selectdb $sql(database) + + unset sql_credentials } #Escape a string to use as sql query parameter diff --git a/Vault.tcl b/Vault.tcl new file mode 100644 --- /dev/null +++ b/Vault.tcl @@ -0,0 +1,22 @@ +package require vault + +proc vault_login {} { + global vault + + ::vault::init $vault(host) + ::vault::appRoleLogin $vault(roleID) $vault(secretID) +} + +proc vault_get {property {key {}}} { + if {[catch {set credential [::vault::readKV apps/viperserv/$property $key]} err]} { + if {[string match "*403 Forbidden*" $err]} { + # Token expired? + vault_login + return [::vault::readKV apps/viperserv/$property $key] + } + } + + return $credential +} + +vault_login diff --git a/Wearg/Broker.tcl b/Wearg/Broker.tcl --- a/Wearg/Broker.tcl +++ b/Wearg/Broker.tcl @@ -17,7 +17,7 @@ } proc connect {} { - mq connect [registry get broker.host] [registry get broker.user] [registry get broker.password] [registry get broker.vhost] + mq connect [registry get broker.host] [vault_get broker username] [vault_get broker password] [registry get broker.vhost] } proc is_timer_started {} { diff --git a/vendor/README.md b/vendor/README.md --- a/vendor/README.md +++ b/vendor/README.md @@ -15,3 +15,4 @@ - bseen1.4.2.tcl - Bass's Seen - provides `!seen` command - proxycheck.tcl - Open proxy checker by James Seward (GPL) + - vault.tcl - Vault API client WIP to be integrated in tcllib (BSD-2-Clause) diff --git a/vendor/vault.tcl b/vendor/vault.tcl new file mode 100644 --- /dev/null +++ b/vendor/vault.tcl @@ -0,0 +1,113 @@ +# -*- tcl -*- +# +# Copyright (c) 2022 by Sébastien Santoro +# +# A client to use HashiCorp Vault through the HTTP API. + +package require http +package require json +package require json::write +package require tls + +::http::register https 443 ::tls::socket + +package provide vault 0.1 + +namespace eval ::vault { + + variable addr + variable token + +} + +### +### Initialize parameters +### + +proc ::vault::init {{address ""}} { + variable addr + variable token + + if {$address == ""} { + # Try to read VAULT_ADDR standard environment variable + if {[info exists env(VAULT_ADDR)]} { + set addr $env(VAULT_ADDR) + return + } + + error "Address must be specified as argument or available in VAULT_ADDR environment variable." + } + + set addr $address + set token "" +} + +proc ::vault::setToken {sessionToken} { + variable token + set token $sessionToken +} + +### +### Helper methods +### + +proc ::vault::request {method url {params {}}} { + variable addr + variable token + + set command [list ::http::geturl $addr$url -method $method] + + if {[llength $params] > 0} { + lappend command -query + lappend command [::vault::payload $params] + } + + if {$token != ""} { + lappend command -headers + lappend command [list X-Vault-Token $token] + } + + set httpToken [{*}$command] + if {[::http::ncode $httpToken] != 200} { + error "Vault returned [::http::code $httpToken], 200 OK was expected." + } + + set response [::json::json2dict [::http::data $httpToken]] + ::http::cleanup $httpToken + return $response +} + +proc ::vault::payload {params} { + ::json::write object {*}[dict map {k v} $params { + set v [::json::write string $v] + }] +} + +proc ::vault::resolveKVPath {path} { + set parts [split $path /] + + return /v1/[lindex $parts 0]/data/[join [lrange $parts 1 end] /] +} + +### +### API methods +### + +proc ::vault::appRoleLogin {roleID secretID} { + set params [list role_id $roleID secret_id $secretID] + set response [::vault::request POST /v1/auth/approle/login $params] + + variable token + set token [dict get [dict get $response auth] client_token] +} + +proc ::vault::readKV {path {key {}}} { + set response [::vault::request GET [::vault::resolveKVPath $path]] + set response [dict get $response data] + + if {$key == ""} { + return $response + } + + dict get [dict get $response data] $key +}