openssl 구성 파일에서 구성 읽기

openssl 구성 파일에서 구성 읽기

쉘 스크립트에서 openssl 구성 값을 읽기 위한 힌트나 솔루션을 찾는 데 어려움을 겪고 있습니다. 좀 더 자세한 내용을 알려드리겠습니다.

나는 가지고있다openssl.conf다음과 같은 단순화된 내용이 포함된 파일:

[ca_one]
dir = /home/auhorities/ca_one
certs = $dir/certs
database = $dir/index.txt
serial = $dir/serial
[ca_two]
dir = /home/auhorities/ca_two
certs = $dir/certs
database = $dir/index.txt
serial = $dir/serial

모든 인증 기관의 dir/certs 디렉토리를 조사하는 bash 스크립트를 작성 중입니다. 즉, 값을 가져와야 합니다./home/auhorities/ca_one/certs그리고/home/auhorities/ca_two/certs파일을 형성합니다. 현재로서는 다음과 같은 해결책을 찾았습니다.

#!/usr/bin/env bash

for certs_dir in $(grep -E ^dir.+ openssl.conf | tr -d ' ' | cut -d '=' -f 2); do
  echo "$certs_dir/certs"
done

하지만 향후 스크립트 기능이 업데이트된다면 이는 이상적이지 않을 것 같습니다. 나는 미래의 나와 동료가 보다 편리한 방식으로 인증 기관 항목을 탐색할 수 있게 해주는 솔루션을 찾고 있습니다. 예를 들어, openssl을 찾았습니다.구성수동(https://www.openssl.org/docs/manmaster/man5/config.html), 즉구성라이브러리는 "구성 파일을 읽는 데 사용할 수 있습니다". 내가 이해하는 한, 이 라이브러리는 다음과 같은 openssl 도구에서 내부적으로 사용됩니다.캘리포니아, 요구또는 기타. 그런 다음 일부 openssl 유틸리티를 사용하여 구성 파일에서 항목을 읽을 수 있습니까? 뭔가 빠졌을 수도 있지만 가능한지 아닌지 알 수 없습니다. 이것이 선택 사항이 아니라면 그러한 상황에 어떻게 대처하시겠습니까?

미리 감사드립니다.

답변1

openssl.cnf를 구문 분석하거나 유효성을 검사하는 공식 명령줄 도구는 없습니다. 그러나 호환되는 기능을 만들었습니다 bash.남자 구성확실히 읽고 또 읽어야 할 내용입니다.

중요: 구성이 중단되면 작동하지 않습니다. 문제를 해결하세요!

openssl.cnf테스트를 위해 예제를 좀 더 지저분하게 만들어야 합니다.

#set this! it forces ${var}/$(var); treats $var as a string literal
.pragma = dollarid:true
#with a huge config you are eventually going to want to section it out
.include my_other_conf.cnf

#safe variable mapping, always use to avoid config errors
DS = / #set a default variable value (DS = \ in Windows)
DS = ${ENV::DS} #value above used if DS isn't mapped in the shell

[ca_one]
  #indent every section for readability
  dir      = /home/auhorities/ca_one
  certs    = $dir/certs
  database = $dir/index.txt
  serial   = $dir/serial

[ca_two]
  dir      = /home/auhorities/ca_two
  certs    = $dir/certs
  database = $dir/index.txt
  serial   = $dir/serial

  [  section_test0    ] #this is just how nasty (but valid) things get
  space_test=   " space  " ' test  '
var_test_ = boop

var_test.0 = ${var_test_}                #local syntax
var_test.1 = $(var_test_)                #alt syntax
var_test.2 = ${section_test0::var_test_} #$section::name syntax
var_test.3 = ${ENV::DS}                  #$ENV::name syntax
dollarid_test = $var_test_               #$dollarid:off only
escape_test = H\C3\A0 N\E1\BB\99i \ \# \\
test_multiline= 123 \\ \ \
    456\
    #789

이제 구성 파일을 삭제하여 주석, 빈 줄, 후행/선행 공백, [ section ]레이블 및 name = value쌍 내부의 공백을 제거합니다.

다음과 name=value같이 결과 쌍을 할당합니다 .SSL_CONF[section,name]=value

#!/bin/bash
declare -A SSL_CONF #initialize our config array
declare SSL_CONF_SECTION #set last section
function ssl_include(){
  local m a id d="$3" c="${1:-$OPENSSL_CONF}" e='a-zA-Z0-9_' #start in last section. dollarid:false (default). set conf file. regex to match sections
  [[ ! -r "$c" ]] && return     #the file isn't readable
  SSL_CONF_SECTION="${2/ /}" #set section
  [ -d "$c" ] && local d='nodir' c="\"${c%/}/*.cnf\" \"${c%/}/*.conf\""    #conf is a dir
  while IFS= read -r l || [ -n "$l" ]; do         #build SSL_CONF array
    l="${l%%[^\\]#*}"              #remove comment
    if [ "$m" != '' ]; then      #last line ended with /
      [[ "$l" =~ [^\\]\\$ ]] && l="${l:0:-1}" || m=''               #check for continued multiline
      SSL_CONF[${SSL_CONF_SECTION// /},${m}]="${SSL_CONF[${SSL_CONF_SECTION// /},${m}]}${l//[^\\]\\$/}" && continue #add current line to last conf and move to next line
    fi
    l="${l#"${l%%[![:space:]]*}"}"; l="${l%"${l##*[![:space:]]}"}"; [[ "$l" == '' ]] && continue #remove leading/trailing whitespace, then skip empty lines
    if [[ "$l" =~ ^\.include[[:space:]]*=[[:space:]]*(.*)$ ]]; then #include additional files
      [ "$d" == 'nodir' ] && continue            #dir loaded conf files cant include further
      local d='no' i="${BASH_REMATCH[1]}" o="${OPENSSL_CONF_INCLUDE:-${c%/*}}" #no variable parsing, store last match, handle default include path
      [[ ! "$i" =~ ^\.{0,2}/ ]] && i="${o%/}/${i}"  #append default path to relative paths
      for f in "$i"; do [ -r "$f" ] && ssl_include "$f" "${SSL_CONF_SECTION// /} " "$d"; done #parse additional configuration files, keeping section
      continue
    fi
    [[ "${SSL_CONF_SECTION// /}" == '' && "$l" =~ ^\.pragma[[:space:]]*=[[:space:]]*(dollarid)[[:space:]]*:[[:space:]]*(true|on)$ ]] && id=${BASH_REMATCH[2]} && continue #see how local variables are parsed
    [[ "$l" =~ ^\[[[:space:]]*([${e}]+)[[:space:]]*\]$ ]] && SSL_CONF_SECTION=${BASH_REMATCH[1]} && continue #set section name
    if [[ "$l" =~ ^([${e},\;.]+)[[:space:]]*=[[:space:]]*(.+)$ ]]; then #name value pair
      local n="${BASH_REMATCH[1]}" v="${BASH_REMATCH[2]}"
      [[ "$v" =~ [^\\]\\$ ]] && o="$n"          #found a multiline value
      SSL_CONF[${SSL_CONF_SECTION// /},${n}]="${v//\\[^nbrt\$\\\#]/}" && continue #add name value to SSL_CONF array
    fi
  done< <(cat $c 2>/dev/null) #loop through the config(s)
}

따른 논리는 다음과 같습니다.

  • 주석, 빈 줄, 선행/후행 공백은 무시됩니다.
  • [ section_name ]name = value내부 공백은 무시됩니다 .
  • .include그리고 ..pragma=이전 버전과의 호환성이 허용되었습니다.
  • .include파일의 어느 곳에나 있을 수 있습니다.
  • .include /dir/*.cnf*.conf정의된 디렉터리에 포함됩니다 . .include포함된 파일의 처리를 비활성화합니다.
  • section이름 은 , , 및 $var로 구성될 수 있습니다 .a-zA-Z0-9_
  • namea-z, A-Z, 0-9, _, ;, .및 으로 구성될 수 있습니다.,
  • value단일(백슬래시)로 끝나는 경우 다음 \줄로 계속됩니다.
  • 일부 시퀀스에서는 \다음과 같이 이스케이프해야 합니다. \\, \\$, \#, \n, \b, \r,\t

이제 다음과 같은 연관 배열이 생겼습니다 SSL_CONF[section,name]=value. 파일은 .include=(.*)발견되는 즉시 재귀적으로 구문 분석됩니다. .pragma=dollarid:true또한 변수를 정확하게 구문 분석할 수 있도록 처리됩니다.

이제 마지막 문제가 하나 생겼습니다.구성 변수. 현재 ${var}, ${section::var}, ${ENV::var}, $var, 로 지정되어 있습니다 $section::var.$ENV::var 그리고 $(var), $(section::var), $(ENV::var)(누가 알았나요?). 운 좋게도 배열을 반복하여 SSL_CONF실제 값을 할당할 수 있습니다.

#!/bin/bash
[ "$id" != '' ] && a='\{'                     #ignore not bracketed variables
local f="[^\\\\]*(\\\$(\(|\{|${a})([${e}]+)(::([${e}]+))?(\)|\}|))" # match $var ${var} $(var) unless preceded by \
for k in "${!SSL_CONF[@]}"; do                #loop through our array looking for variables
  local o #last value placeholder
  while [ "${SSL_CONF[$k]}" != "$o" ]; do     #only loop if the variable changed
    o="${SSL_CONF[$k]}"                       #set loop to exit on no change
    if [[ "${SSL_CONF[$k]}" =~ $f ]] && \
     [[ "${BASH_REMATCH[2]}${BASH_REMATCH[6]}" == '' || "${BASH_REMATCH[2]}${BASH_REMATCH[6]}" == '()' ||  "${BASH_REMATCH[2]}${BASH_REMATCH[6]}" == '{}' ]] #brackets match
    then                                      #the value contains a variable
      local r=' #'                            #replacement indicator (illegal value)
      [[ "$r" == ' #' && ${SSL_CONF[${k%%,*},${BASH_REMATCH[3]}]+isset} ]]             && r="${SSL_CONF[${k%%,*},${BASH_REMATCH[3]}]}"              #local variable
      [[ "$r" == ' #' && ${SSL_CONF[default,${BASH_REMATCH[3]}]+isset} ]]              && r="${SSL_CONF[default,${BASH_REMATCH[3]}]}"               #'default' variable
      [[ "$r" == ' #' && ${SSL_CONF[,${BASH_REMATCH[3]}]+isset} ]]                     && r="${SSL_CONF[,${BASH_REMATCH[3]}]}"                      #default variable
      if [ "${BASH_REMATCH[5]}" != '' ]; then #variable is from another section, default, or ENV
        [[ "$r" == ' #' && "${BASH_REMATCH[3]}" == "ENV" ]]                            && r="${!BASH_REMATCH[5]:-${SSL_CONF[,${BASH_REMATCH[5]}]}}" #environment variable
        [[ "$r" == ' #' && ${SSL_CONF[${BASH_REMATCH[3]},${BASH_REMATCH[5]}]+isset} ]] && r="${SSL_CONF[${BASH_REMATCH[3]},${BASH_REMATCH[5]}]}"    #section variable
      fi
      [ "$r" != ' #' ] && SSL_CONF[$k]="${SSL_CONF[$k]//${BASH_REMATCH[1]}/$r}" #replace our variable with the value
    fi
  done
done

이제 모든 변수가 계산된 값이 됩니다!

전체 기능과 예제 코드는 다음과 같습니다.

#!/bin/bash
declare -A SSL_CONF #initialize our config array
declare SSL_CONF_SECTION #set last section
function ssl_include(){
  local m a id d="$3" c="${1:-$OPENSSL_CONF}" e='a-zA-Z0-9_' #start in last section. dollarid:false (default). set conf file. regex to match sections
  [[ ! -r "$c" ]] && return     #the file isn't readable
  SSL_CONF_SECTION="${2/ /}" #set section
  [ -d "$c" ] && local d='nodir' c="\"${c%/}/*.cnf\" \"${c%/}/*.conf\""    #conf is a dir
  while IFS= read -r l || [ -n "$l" ]; do         #build SSL_CONF array
    l="${l%%[^\\]#*}"              #remove comment
    if [ "$m" != '' ]; then      #last line ended with /
      [[ "$l" =~ [^\\]\\$ ]] && l="${l:0:-1}" || m=''               #check for continued multiline
      SSL_CONF[${SSL_CONF_SECTION// /},${m}]="${SSL_CONF[${SSL_CONF_SECTION// /},${m}]}${l//[^\\]\\$/}" && continue #add current line to last conf and move to next line
    fi
    l="${l#"${l%%[![:space:]]*}"}"; l="${l%"${l##*[![:space:]]}"}"; [[ "$l" == '' ]] && continue #remove leading/trailing whitespace, then skip empty lines
    if [[ "$l" =~ ^\.include[[:space:]]*=[[:space:]]*(.*)$ ]]; then #include additional files
      [ "$d" == 'nodir' ] && continue            #dir loaded conf files cant include further
      local d='no' i="${BASH_REMATCH[1]}" o="${OPENSSL_CONF_INCLUDE:-${c%/*}}" #no variable parsing, store last match, handle default include path
      [[ ! "$i" =~ ^\.{0,2}/ ]] && i="${o%/}/${i}"  #append default path to relative paths
      for f in "$i"; do [ -r "$f" ] && ssl_include "$f" "${SSL_CONF_SECTION// /} " "$d"; done #parse additional configuration files, keeping section
      continue
    fi
    [[ "${SSL_CONF_SECTION// /}" == '' && "$l" =~ ^\.pragma[[:space:]]*=[[:space:]]*(dollarid)[[:space:]]*:[[:space:]]*(true|on)$ ]] && id=${BASH_REMATCH[2]} && continue #see how local variables are parsed
    [[ "$l" =~ ^\[[[:space:]]*([${e}]+)[[:space:]]*\]$ ]] && SSL_CONF_SECTION=${BASH_REMATCH[1]} && continue #set section name
    if [[ "$l" =~ ^([${e},\;.]+)[[:space:]]*=[[:space:]]*(.+)$ ]]; then #name value pair
      local n="${BASH_REMATCH[1]}" v="${BASH_REMATCH[2]}"
      [[ "$v" =~ [^\\]\\$ ]] && o="$n"          #found a multiline value
      SSL_CONF[${SSL_CONF_SECTION// /},${n}]="${v//\\[^nbrt\$\\\#]/}" && continue #add name value to SSL_CONF array
    fi
  done< <(cat $c 2>/dev/null) #loop through the config(s)
  [ "$d" != '' ] && return         #don't parse variables in included files, just return the section name
[ "$id" != '' ] && a='\{'                     #ignore not bracketed variables
local f="[^\\\\]*(\\\$(\(|\{|${a})([${e}]+)(::([${e}]+))?(\)|\}|))" # match $var ${var} $(var) unless preceded by \
for k in "${!SSL_CONF[@]}"; do                #loop through our array looking for variables
  local o #last value placeholder
  while [ "${SSL_CONF[$k]}" != "$o" ]; do     #only loop if the variable changed
    o="${SSL_CONF[$k]}"                       #set loop to exit on no change
    if [[ "${SSL_CONF[$k]}" =~ $f ]] && \
     [[ "${BASH_REMATCH[2]}${BASH_REMATCH[6]}" == '' || "${BASH_REMATCH[2]}${BASH_REMATCH[6]}" == '()' ||  "${BASH_REMATCH[2]}${BASH_REMATCH[6]}" == '{}' ]] #brackets match
    then                                      #the value contains a variable
      local r=' #'                            #replacement indicator (illegal value)
      [[ "$r" == ' #' && ${SSL_CONF[${k%%,*},${BASH_REMATCH[3]}]+isset} ]]             && r="${SSL_CONF[${k%%,*},${BASH_REMATCH[3]}]}"              #local variable
      [[ "$r" == ' #' && ${SSL_CONF[default,${BASH_REMATCH[3]}]+isset} ]]              && r="${SSL_CONF[default,${BASH_REMATCH[3]}]}"               #'default' variable
      [[ "$r" == ' #' && ${SSL_CONF[,${BASH_REMATCH[3]}]+isset} ]]                     && r="${SSL_CONF[,${BASH_REMATCH[3]}]}"                      #default variable
      if [ "${BASH_REMATCH[5]}" != '' ]; then #variable is from another section, default, or ENV
        [[ "$r" == ' #' && "${BASH_REMATCH[3]}" == "ENV" ]]                            && r="${!BASH_REMATCH[5]:-${SSL_CONF[,${BASH_REMATCH[5]}]}}" #environment variable
        [[ "$r" == ' #' && ${SSL_CONF[${BASH_REMATCH[3]},${BASH_REMATCH[5]}]+isset} ]] && r="${SSL_CONF[${BASH_REMATCH[3]},${BASH_REMATCH[5]}]}"    #section variable
      fi
      [ "$r" != ' #' ] && SSL_CONF[$k]="${SSL_CONF[$k]//${BASH_REMATCH[1]}/$r}" #replace our variable with the value
    fi
  done
done
}

용법:ssl_include <file/dir>

원하는 두 값은 ${SSL_CONF[ca_one,certs]}and 에 있습니다 ${SSL_CONF[ca_two,certs]}(예제 스크립트에서는 이러한 값을 반영합니다).

사소한 참고사항:

  • bash -version5.03.3(1), openssl version오픈SSL 1.1.1g
  • wc -l openssl.cnf824, grep -c '[^\\]=.*[^\\]\$' openssl.cnf167,time ./ssl_include.sh openssl.cnf 0분0.854초
  • $var참조된 시간까지 할당되지 않으면 실패 openssl합니다.스크립트는 그렇지 않습니다
  • 섹션 이전에 설정된 모든 구성 옵션은 로 설정됩니다 SSL_CONF[,name]. 이는 과의 충돌을 방지합니다 [default]. [default]확인됨첫 번째.
  • 유효하지 않은 이스케이프 시퀀스~해야 한다삭제되다제대로, 하지만openssl 버그가 있습니다( 의 "버그" 참조 man config).그게 내가 확인하는 걸 방해하는 거야
  • .pragma으로 작업 할 수 없으므로 openssl구현을 확인할 수 없습니다..pragma이 메모를 삭제할 수 있도록 누군가 저에게 작동하는 구성을 만들어 줄 수 있나요 ?
  • 아직 테스트되지 않았습니다 libressl.
  • openssl.import선언된 파일에 an을 연결합니다 . 가져오기가 정의되어 있으면 앞으로 [section]나아갈 것입니다 [section].많은 혼란스러운 오류가 발생합니다.

관련 정보