쉘 스크립트에서 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-z
A-Z
0-9
_
name
a-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]}
( ${SSL_CONF[ca_two,certs]}
샘플 스크립트에서 이러한 값을 반영함)입니다.
작은 메모:
bash -version
5.03.3(1),openssl version
오픈SSL 1.1.1gwc -l openssl.cnf
824, 167grep -c '[^\\]=.*[^\\]\$' openssl.cnf
,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]
앞으로 나아갈 수 있습니다.혼란스러운 오류가 많이 발생했습니다.