토큰 헬퍼 사용자 지정

토큰 헬퍼 사용자 지정 (Use a custom token helper)

토큰 헬퍼(token helper)는 저장된 인증 토큰을 저장(save), 검색(retrieve), 삭제(erase)하는 프로그램이나 스크립트입니다. 기본적으로 Vault CLI에는 활성화된 인증 백엔드의 토큰을 ~/.vault-token 파일에 캐시하는 토큰 헬퍼가 포함되어 있습니다. 사용자 지정 토큰 헬퍼로 이 캐싱 동작을 사용자 지정할 수 있습니다.

출처: 문서

본문

1단계: 헬퍼 스크립트 작성 (Step 1: Script your helper)

토큰 헬퍼는 단일 명령줄 인자를 받아야 합니다:

인자 동작
get 캐시된 인증 토큰을 가져와 stdout으로 인쇄
store stdin에서 인증 토큰을 읽어 안전한 위치에 저장
erase 캐시된 인증 토큰을 삭제

인증 토큰은 원하는 방식으로 관리할 수 있지만, 헬퍼는 다음 출력 요구 사항을 지켜야 합니다:

  • stdout 쓰기는 토큰 문자열로 제한합니다.
  • 모든 오류 메시지는 stderr 에 기록합니다.
  • 오류도 토큰도 아닌 모든 출력은 syslog 또는 로그 파일에 기록합니다.
  • 성공 시 상태 코드 0 을 반환합니다.
  • 오류 시 0이 아닌 상태 코드를 반환합니다.

2단계: Vault 구성 (Step 2: Configure Vault)

사용자 지정 토큰 헬퍼를 구성하려면 홈 디렉터리 아래의 .vault 라는 CLI 구성 파일을 편집(또는 생성)하고, 새 헬퍼의 전체 경로로 token_helper 파라미터를 설정하세요:

Linux 셸:

echo 'token_helper = "/path/to/token/helper.sh"' >> ${HOME}/.vault

Powershell:

'token_helper = "\\path\\to\\token\\helper.ps1"' | `
Out-File -FilePath ${env:USERPROFILE}/.vault -Encoding ascii -Append

팁: Vault가 구성 파일을 읽을 때 잘못된 문자에 불평하지 않도록 UTF-8 인코딩(ascii)을 사용하세요.

팁: 스크립트가 Vault 바이너리로 실행 가능하도록 해야 합니다.

예제 토큰 헬퍼 (Example token helper)

다음 토큰 헬퍼는 홈 디렉터리의 .vault_tokens 라는 JSON 파일에서 토큰을 관리합니다. 헬퍼는 $VAULT_ADDR 환경 변수를 사용해 서로 다른 Vault 서버의 토큰을 저장하고 검색합니다.

Shell:

#!/bin/bash

function write_error(){ >&2 echo $@; }

# Customize the hash key for tokens. Currently, we remove the strings
# 'https://', '.', and ':' from the passed address (Vault address environment
# by default) because jq has trouble with special characeters in JSON field
# names
function createHashKey {
  
  local key=""

  if [[ -z "${1}" ]] ; then key="${VAULT_ADDR}" 
  else                      key="${1}"
  fi
  
  # We index the token according to the Vault server address by default so
  # return an error if the address is empty
  if [[ -z "${key}" ]] ; then
    write_error "Error: VAULT_ADDR environment variable unset."
    exit 100
  fi

  key=${key//"http://"/""}
  key=${key//"."/"_"}
  key=${key//":"/"_"}

  echo "addr-${key}"
}

TOKEN_FILE="${HOME}/.vault_token"
KEY=$(createHashKey)
TOKEN="null"

# If the token file does not exist, create it
if [ ! -f ${TOKEN_FILE} ] ; then
   echo "{}" > ${TOKEN_FILE}
fi

case "${1}" in
    "get")

      # Read the current JSON data and pull the token associated with ${KEY}
      TOKEN=$(cat ${TOKEN_FILE} | jq --arg key "${KEY}" -r '.[$key]')
      
      # If the token != to the string "null", print the token to stdout 
      # jq returns "null" if the key was not found in the JSON data
      if [ ! "${TOKEN}" == "null" ] ; then
        echo "${TOKEN}"
      fi
      exit 0
    ;;

    "store")
      
      # Get the token from stdin
      read TOKEN

      # Read the current JSON data and add a new entry
      JSON=$(
        jq                      \
        --arg key "${KEY}"      \
        --arg token "${TOKEN}"  \
        '.[$key] = $token' ${TOKEN_FILE}
      )
      
    ;;

    "erase")
      # Read the current JSON data and remove the entry if it exists
      JSON=$(
        jq                      \
        --arg key "${KEY}"      \
        --arg token "${TOKEN}"  \
        'del(.[$key])' ${TOKEN_FILE}
      )
    
    ;;

    *)
      # change to stderr for real code
      write_error "Error: Provide a valid command: get, store, or erase."
      exit 101
esac

# Update the JSON file and return success
echo $JSON | jq "." > ${TOKEN_FILE}
exit 0

PowerShell:

<#
.Synopsis
  Vault token helper script
.INPUTS
  Positional/command line argument: get, store, erase
.OUTPUTS
  get: prints a cached authentication token to stdin (if it exists)
  store: no output, updates the token cache
  erase: no output, updates the token cache
#>

<#
.Synopsis
  CreateHashKey
.DESCRIPTION
  Customize the hash key for tokens. Currently, we remove the strings
  'https://', '.', and ':' from the passed address (Vault address environment by
  default) variable to simplify the hash key string
#>
function CreateHashKey {

  Param($address = "${env:VAULT_ADDR}")
  
  # We index the token according to the Vault server address by default so
  # return an error if the address is empty
  if ( -not $address) {
    Write-Error "[Missing value] env:VAULT_ADDR currently unset."
    exit 101
  }

  $key = ${address}.Replace("/","").Replace(".","_").Replace(":","_")
  return ${key}.Replace("http_", "addr-")
}

<#
.Synopsis
  GetTokenCache
.DESCRIPTION
  Read in or create a new token cache and initialize the hash 
#>
function GetTokenCache {

  Param($filename)

  # Read the JSON file (token cache) and initialize the hash data or create an
  # empty hash if the file does not exist yet
  if ( Get-Item -Path "./${filename}" -ErrorAction SilentlyContinue ) {
    $fileData = (Get-Content "${filename}" -Raw | ConvertFrom-Json -AsHashtable)
  } else {
    $fileData = (Write-Output "{}" | ConvertFrom-Json -AsHashtable)
  } 

  return $fileData
}

<#
.Synopsis
  UpdateTokenCache
.DESCRIPTION
  Write the token hash out to the cache 
#>
function UpdateTokenCache {

  Param($filename, $fileData)
  $jsonData = ($fileData | ConvertTo-Json) 

  # Convert the hash to JSON and update the token cache
  $jsonData | Out-File -Encoding ascii "${filename}"

  return
}

$tokenFile = "${env:USERPROFILE}/.vault_token"
$hashData  = (GetTokenCache "${tokenFile}")
$key       = (CreateHashKey)
$token     = $null

switch -Exact -CaseSensitive (${args}[0]) {

  "get" {
    # Print the token to stdin and return success
    Write-Output ${hashData}.${key}
    exit 0
  }

  "store" {
    $token = Read-Host
    # Add the new token to the hash
    $hashData["${key}"] = "${token}" 
  }

  "erase" {
    # Erase the token entry if it exists
    if ($hashData.ContainsKey("${key}") ) {
      $hashData.Remove("${key}")
    }
  }

  Default {
    # The argument was invalid so return an error
    Write-Error "[Invalid argument] Command must be: get, store, or erase."
    exit 102
  }

}

# Update the token cache and return success
UpdateTokenCache ${tokenFile} ${hashData}

exit 0

Ruby:

#!/usr/bin/env ruby

require 'json'

# We index the token according to the Vault server address
# so the VAULT_ADDR variable is required 
unless ENV['VAULT_ADDR']
  STDERR.puts "No VAULT_ADDR environment variable set. Set it and run me again!"
  exit 100
end

# If the token file does not exist, create and initialize the hashmap
begin
  tokens = JSON.parse(File.read("#{ENV['HOME']}/.vault_tokens"))
rescue Errno::ENOENT => e
  # file doesn't exist so create a blank hash for it
  tokens = {}
end

# Get the first command line argument
case ARGV.first
when 'get'
  # Write the token to stdout if it exists
  print tokens[ENV['VAULT_ADDR']] if tokens[ENV['VAULT_ADDR']]
  exit 0
when 'store'
  # Read the token from stdin
  tokens[ENV['VAULT_ADDR']] = STDIN.read
when 'erase'
  # Delete the token entry if it exists
  tokens.delete(ENV['VAULT_ADDR'])
end

# Update the token file
File.open("#{ENV['HOME']}/.vault_tokens", 'w') { |file| file.write(tokens.to_json) }

더 알아보기 (Learn more)