Konfiguration aus einer OpenSSL-Konfigurationsdatei lesen

Konfiguration aus einer OpenSSL-Konfigurationsdatei lesen

Ich habe Schwierigkeiten, einen Hinweis oder eine Lösung zum Lesen von OpenSSL-Konfigurationswerten in einem Shell-Skript zu finden. Lassen Sie mich Ihnen ein paar weitere Details geben.

ich habe einopenssl.confDatei mit folgendem vereinfachten Inhalt:

[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

Ich schreibe ein Bash-Skript, das im Verzeichnis dir/certs nach jeder Zertifizierungsstelle sucht, d. h. ich muss Werte abrufen/home/behörden/ca_one/certsUnd/home/behörden/ca_two/certsaus der Datei. Vorerst bin ich bei der folgenden Lösung gelandet:

#!/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

Ich befürchte jedoch, dass dies nicht ideal ist, wenn die Skriptfunktionalität in Zukunft aktualisiert wird. Ich suche nach einer Lösung, die es mir oder meinen Kollegen in Zukunft ermöglicht, bequemer durch die Einträge der Zertifizierungsstelle zu navigieren. Ich habe beispielsweise openssl gefunden.KonfigurationHandbuch(https://www.openssl.org/docs/manmaster/man5/config.html), die besagt, dass dieKonfigurationBibliothek "kann zum Lesen von Konfigurationsdateien verwendet werden". Soweit ich weiß, wird diese Bibliothek intern von OpenSSL-Tools wieca, Anforderungoder etwas anderes. Ist es dann möglich, die Einträge aus der Konfigurationsdatei mit einigen der OpenSSL-Dienstprogramme zu lesen? Ich übersehe wahrscheinlich etwas, aber ich kann einfach nicht herausfinden, ob es möglich ist oder nicht. Wenn dies keine Option ist, wie würden Sie mit einer solchen Situation umgehen?

Vielen Dank im Voraus.

Antwort1

Es gibt kein offizielles Befehlszeilentool zum Parsen oder Validieren von openssl.cnf. Ich habe bashjedoch eine kompatible Funktion erstellt.man configist sicherlich etwas, das Sie lesen und wieder lesen müssen.

WICHTIG: Wenn Ihre Konfiguration kaputt ist, funktioniert das nicht. Beheben Sie das Problem!

Das Beispiel openssl.cnfmuss zu Testzwecken etwas ausführlicher sein:

#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

Bereinigen Sie nun die Konfigurationsdatei – entfernen Sie Kommentare, Leerzeilen, nachstehende/vorangestellte Leerzeichen und Leerzeichen innerhalb von [ section ]Beschriftungen und name = valuePaaren.

Ordnen Sie die resultierenden name=valuePaare wie SSL_CONF[section,name]=valuefolgt zu:

#!/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)
}

Die folgende Logik war:

  • Kommentare, Leerzeilen, führende/nachgestellte Leerzeichen werden ignoriert
  • [ section_name ]und name = valueinterne Leerzeichen werden ignoriert
  • .includeund .pragmaerfordern kein =. Es wurde aus Gründen der Abwärtskompatibilität zugelassen
  • .includekann sich überall in der Datei befinden
  • .include /dir/enthält *.cnfund *.confim definierten Verzeichnis. Es deaktiviert .includedie Verarbeitung in den enthaltenen Dateien
  • sectionund $varNamen können aus a-z, A-Z, 0-9, und bestehen._
  • namekann bestehen aus a-z, A-Z, 0-9, _, ;, ., und,
  • eine valueEndung mit einem einzelnen \(Backslash) Weiter zur nächsten Zeile
  • Einige Sequenzen müssen \folgendermaßen maskiert werden: \\, \\$, \#, \n, \b, \r,\t

Jetzt haben Sie ein assoziatives Array wie SSL_CONF[section,name]=value. Die .include=(.*)Dateien werden rekursiv analysiert, sobald sie gefunden werden. .pragma=dollarid:truewird ebenfalls verarbeitet, sodass Sie Variablen genau analysieren können.

Jetzt haben Sie noch ein letztes Problem:KONFIGURATIONSVARIABLEN. Sie sind derzeit zugewiesen als ${var}, ${section::var}, ${ENV::var}, $var, $section::var,$ENV::var UND $(var),, $(section::var)( $(ENV::var)wer hätte das gedacht?). Glücklicherweise können wir unser Array durchlaufen SSL_CONFund die tatsächlichen Werte zuweisen:

#!/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

Und jetzt sind alle Ihre Variablen ihre berechneten Werte!

Hier ist die vollständige Funktion und der Beispielcode:

#!/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
}

Verwendung:ssl_include <file/dir>

Ihre beiden gewünschten Werte befinden sich in ${SSL_CONF[ca_one,certs]}und ${SSL_CONF[ca_two,certs]}(das Beispielskript gibt diese Werte wieder).

Kleinere Hinweise:

  • bash -version5.03.3(1), openssl versionOpenSSL 1.1.1g
  • wc -l openssl.cnf824, grep -c '[^\\]=.*[^\\]\$' openssl.cnf167,time ./ssl_include.sh openssl.cnf 0m0.854s
  • Wenn $vardie Zuweisung zum Zeitpunkt der Referenzierung nicht erfolgt, openssltritt ein Fehler auf.Das Skript
  • alle Konfigurationsoptionen, die vor einem Abschnitt gesetzt werden, werden als gesetzt SSL_CONF[,name]. Dies vermeidet Kollisionen mit [default]. [default]ist aktiviertErste.
  • jede ungültige Escape-Sequenzsollengelöscht werdenrichtig, AberEs liegt ein OpenSSL-Fehler vor (siehe „Fehler“ in man config)das hindert mich daran, zu überprüfen
  • kann nicht .pragmadamit arbeiten openssl, daher kann ich meine Implementierung nicht überprüfen.kann mir jemand eine funktionierende .pragmaKonfiguration erstellen, damit ich diese Notiz entfernen kann?
  • noch nicht getestet auf libressl, etc
  • opensslfügt ein .importin die Datei ein, in der es deklariert wurde. Wenn Ihr Import ein [section]definiert hat, befinden Sie sich im [section]weiteren Verlauf darin.Es ist für viele verwirrende Fehler verantwortlich

verwandte Informationen