Gebruik het configuratiebestand voor mijn shellscript

Ik moet een configuratiebestand maken voor mijn eigen script:
Hier is een voorbeeld:

script:

#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2 

Inhoud van /home/myuser/test/config:

nam="Mark" sur="Brown" 

dat werkt!

Mijn vraag: is dit de juiste manier om doe dit of zijn er “zijn andere manieren?

Reacties

  • De variabelen moeten bovenaan staan. I ‘ m verbaasd dat het werkt. Hoe dan ook, waarom heb je een configuratiebestand nodig? Ben je van plan om deze variabelen ergens anders te gebruiken?
  • Faheem, ik heb de variabelen nodig omdat mijn script veel opties heeft: een config-bestand zal het script semplificeren. Bedankt
  • IMHO het is prima. Ik zou op deze manier doen.
  • abcde doet het ook op deze manier en dat is een vrij groot programma (voor een shellscript). Je kunt het hier bekijken.

Answer

source is niet veilig omdat het willekeurige code zal uitvoeren. Dit is misschien geen probleem voor u, maar als de bestandsrechten onjuist zijn, kan het voor een aanvaller met bestandssysteemtoegang mogelijk zijn om code uit te voeren als een geprivilegieerde gebruiker door code te injecteren in een configuratiebestand dat is geladen door een anderszins beveiligd script, zoals een init-script.

Tot dusverre is de beste oplossing die ik “heb kunnen identificeren de onhandige oplossing om het wiel opnieuw uit te vinden:

myscript.conf

password=bar echo rm -rf / PROMPT_COMMAND="echo "Sending your last command $(history 1) to my email"" hostname=localhost; echo rm -rf / 

Met source, dit zou echo rm -rf / twee keer draaien, en ook de actieve gebruiker “s $PROMPT_COMMAND veranderen. Doe in plaats daarvan dit:

myscript.sh (Bash 4)

#!/bin/bash typeset -A config # init array config=( # set default values in config array [username]="root" [password]="" [hostname]="localhost" ) while read line do if echo $line | grep -F = &>/dev/null then varname=$(echo "$line" | cut -d "=" -f 1) config[$varname]=$(echo "$line" | cut -d "=" -f 2-) fi done < myscript.conf echo ${config[username]} # should be loaded from defaults echo ${config[password]} # should be loaded from config file echo ${config[hostname]} # includes the "injected" code, but it"s fine here echo ${config[PROMPT_COMMAND]} # also respects variables that you may not have # been looking for, but they"re sandboxed inside the $config array 

myscript.sh (Mac / Bash 3-compatibel)

#!/bin/bash config() { val=$(grep -E "^$1=" myscript.conf 2>/dev/null || echo "$1=__DEFAULT__" | head -n 1 | cut -d "=" -f 2-) if [[ $val == __DEFAULT__ ]] then case $1 in username) echo -n "root" ;; password) echo -n "" ;; hostname) echo -n "localhost" ;; esac else echo -n $val fi } echo $(config username) # should be loaded from defaults echo $(config password) # should be loaded from config file echo $(config hostname) # includes the "injected" code, but it"s fine here echo $(config PROMPT_COMMAND) # also respects variables that you may not have # been looking for, but they"re sandboxed inside the $config array 

Reageer als je een beveiligingsprobleem in mijn code aantreft.

Opmerkingen

  • Ter info: dit is een Bash-versie 4.0-oplossing die helaas onderhevig is aan krankzinnige licentieproblemen opgelegd door Apple en die niet standaard beschikbaar is op Macs
  • @Sukima Goed punt. Ik ‘ heb een versie toegevoegd die compatibel is met Bash 3. Het zwakke punt is dat het * in invoer niet correct kan verwerken, maar dan wat in Bash verwerkt dat karakter goed?
  • Het eerste script mislukt als het wachtwoord een backslash bevat.
  • @Kusalananda Wat als de backslash wordt ontsnapt? my\\password
  • Deze versie heeft enkele seconden nodig om mijn configuratiebestand te verwerken, ik vond de oplossing van gw0 veel sneller.

Answer

Parseer het configuratiebestand, voer het niet uit.

Ik schrijf momenteel een applicatie op het werk die gebruikt een uiterst eenvoudige XML-configuratie:

<config> <username>username-or-email</username> <password>the-password</password> </config> 

In het shellscript (de “applicatie”) is dit wat ik doe om bij de gebruikersnaam te komen (meer of less, ik heb het in een shell-functie gestopt):

username="$( xml sel -t -v "/config/username" "$config_file" )" 

Het xml commando is XMLStarlet , die beschikbaar is voor de meeste Unices.

Ik gebruik XML omdat andere delen van de applicatie ook gegevens verwerken die in XML-bestanden zijn gecodeerd, dus het was het gemakkelijkst.

Als u de voorkeur geeft aan JSON, is er “s jq wat een eenvoudig te gebruiken shell JSON-parser.

Mijn configuratiebestand zou er in JS ongeveer zo uitzien AAN:

{ "username": "username-or-email", "password": "the-password" } 

En dan “zou ik de gebruikersnaam in het script krijgen:

username="$( jq -r ".username" "$config_file" )" 

Opmerkingen

  • Het uitvoeren van het script heeft een aantal voor- en nadelen. De belangrijkste nadelen zijn beveiliging: als iemand het configuratiebestand kan wijzigen, kan hij code uitvoeren en is het moeilijker om het idiootbestendig te maken. De voordelen zijn snelheid, op een eenvoudige test is het meer dan 10.000 keer sneller om een configuratiebestand te vinden dan om pq uit te voeren, en flexibiliteit, iedereen die van Monkey Patching Python houdt, zal dit waarderen.
  • @icarus Hoe groot configuratiebestanden kom je gewoonlijk tegen, en hoe vaak moet je ze in één sessie ontleden? Merk ook op dat er in één keer meerdere waarden uit XML of JSON kunnen zijn gehaald.
  • Gewoonlijk slechts een paar (1 tot 3) waarden. Als je eval gebruikt om meerdere waarden in te stellen, dan voer je geselecteerde delen van het configuratiebestand uit :-).
  • @icarus Ik dacht aan arrays … U hoeft niets eval te gebruiken. De prestatiehit van het gebruik van een standaardformaat met een bestaande parser (ook al is het ‘ een extern hulpprogramma) is verwaarloosbaar in vergelijking met de robuustheid, de hoeveelheid code, het gebruiksgemak en onderhoudbaarheid.
  • +1 voor ” ontleed het configuratiebestand, don ‘ om het uit te voeren ”

Answer

Hier is een schone en draagbare versie die compatibel is met Bash 3 en up, zowel op Mac als Linux.

Het specificeert alle standaardinstellingen in een apart bestand, om de noodzaak van een enorme, rommelige, gedupliceerde “defaults” -configuratiefunctie in al je shellscripts te vermijden. En het laat je kiezen tussen lezen met of zonder standaard fallbacks:

config.cfg :

myvar=Hello World 

config.cfg.defaults :

myvar=Default Value othervar=Another Variable 

config.shlib (dit is een bibliotheek, dus er is geen shebang-line):

config_read_file() { (grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d "=" -f 2-; } config_get() { val="$(config_read_file config.cfg "${1}")"; if [ "${val}" = "__UNDEFINED__" ]; then val="$(config_read_file config.cfg.defaults "${1}")"; fi printf -- "%s" "${val}"; } 

test.sh (of scripts waar u lees configuratiewaarden) :

#!/usr/bin/env bash source config.shlib; # load the config library functions echo "$(config_get myvar)"; # will be found in user-cfg printf -- "%s\n" "$(config_get myvar)"; # safer way of echoing! myvar="$(config_get myvar)"; # how to just read a value without echoing echo "$(config_get othervar)"; # will fall back to defaults echo "$(config_get bleh)"; # "__UNDEFINED__" since it isn"t set anywhere 

Uitleg van het testscript:

  • Merk op dat alle toepassingen van config_get in test.sh tussen dubbele aanhalingstekens staan. Door elke config_get tussen dubbele aanhalingstekens te plaatsen, zorgen we ervoor dat tekst in de variabele waarde nooit verkeerd wordt geïnterpreteerd als vlaggen. En het zorgt ervoor dat we witruimte correct behouden, zoals meerdere spaties achter elkaar in de configuratiewaarde.
  • En wat is die regel printf? “is iets waar je op moet letten: echo is een slecht commando voor het afdrukken van tekst waar je geen controle over hebt. Zelfs als u dubbele aanhalingstekens gebruikt, worden vlaggen geïnterpreteerd. Stel myvar (in config.cfg) in op -e en je ziet een lege regel, omdat echo zal denken dat het “een vlag is. Maar printf heeft dat probleem niet. De printf -- zegt “print this, en” interpreteer niets als vlaggen “, en de "%s\n" zegt” formatteer de uitvoer als een string met een nieuwe regel aan het einde, en tot slot is de laatste parameter de waarde die printf moet opmaken.
  • Als je geen waarden op het scherm wilt weergeven, zou je ze gewoon normaal toewijzen, zoals myvar="$(config_get myvar)";. Als je ze naar het scherm gaat afdrukken, raad ik aan om printf te gebruiken om volledig veilig te zijn tegen echo-incompatibele strings die in de gebruikersconfiguratie kunnen staan. Maar echo is prima als de door de gebruiker verstrekte variabele niet is het eerste teken van de tekenreeks die u gebruikt, aangezien dat “de enige situatie is waarin” vlaggen “kunnen worden geïnterpreteerd, dus zoiets als echo "foo: $(config_get myvar)"; is veilig, aangezien de” foo “begint” niet met een streepje en vertelt daarom echo dat de rest van de string er ook geen vlag voor is. 🙂

Opmerkingen

  • @ user2993656 Bedankt dat je hebt opgemerkt dat mijn originele code nog steeds mijn persoonlijke configuratiebestandsnaam (environment.cfg) bevatte in plaats van de juiste. Wat betreft de ” echo -n ” bewerking die je hebt gedaan, dat hangt af van de gebruikte shell. Op Mac / Linux Bash, ” echo -n ” betekent ” echo zonder nieuwe regel “, wat ik deed om te voorkomen dat er nieuwe regels achter zouden lopen. Maar het lijkt zonder dit precies hetzelfde te werken, dus bedankt voor de bewerkingen!
  • Eigenlijk heb ik het gewoon doorgenomen en herschreven om printf te gebruiken in plaats van echo, wat ervoor zorgt dat we ‘ Ik zal het risico wegnemen dat echo ” vlaggen ” in de configuratiewaarden verkeerd interpreteert.
  • Ik hou echt van deze versie. Ik heb de config.cfg.defaults laten vallen in plaats van ze te definiëren op het moment dat ik $(config_get var_name "default_value") aanriep. tritarget.org/static/…
  • Evenzo – dit is geweldig.

Antwoord

De meest gebruikelijke, efficiënte en correcte manier is om source, of . als een verkorte vorm. Bijvoorbeeld:

source /home/myuser/test/config 

of

. /home/myuser/test/config 

Iets om te overwegen is echter de beveiligingsproblemen die het gebruik van een extra extern afkomstig configuratiebestand kan veroorzaken, aangezien er aanvullende code kan worden ingevoegd. Voor meer informatie, waaronder hoe u dit probleem kunt detecteren en oplossen, raad ik u aan een kijkje te nemen in het gedeelte “Beveiligen” van http://wiki.bash-hackers.org/howto/conffile#secure_it

Reacties

  • Ik had hoge verwachtingen van dat artikel (kwam ook voor in mijn zoekresultaten), maar de auteur ‘ s suggestie om te proberen regex te gebruiken om kwaadaardige code eruit te filteren is een oefening in zinloosheid.
  • De procedure met punt, vereist een absoluut pad?Met de relatieve werkt het niet ‘ niet

Antwoord

Ik gebruik dit in mijn scripts:

sed_escape() { sed -e "s/[]\/$*.^[]/\\&/g" } cfg_write() { # path, key, value cfg_delete "$1" "$2" echo "$2=$3" >> "$1" } cfg_read() { # path, key -> value test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" | sed "s/^$(echo "$2" | sed_escape)=//" | tail -1 } cfg_delete() { # path, key test -f "$1" && sed -i "/^$(echo $2 | sed_escape).*$/d" "$1" } cfg_haskey() { # path, key test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" > /dev/null } 

Moet elke tekencombinatie ondersteunen, behalve dat sleutels “t = in hen, want dat is de scheidingsteken. Al het andere werkt.

% cfg_write test.conf mykey myvalue % cfg_read test.conf mykey myvalue % cfg_delete test.conf mykey % cfg_haskey test.conf mykey || echo "It"s not here anymore" It"s not here anymore 

Dit is ook volkomen veilig omdat het geen source of eval.

Reacties

  • Is niet ‘ dit veronderstelde om eerst het configuratiebestand te maken als het niet ‘ niet bestaat? Het niet ‘ t, maar touch -a "${path}" zal er natuurlijk voor zorgen dat het bestaat, ook zonder de mtime frivool bij te werken.

Answer

Dit is beknopt en veilig:

# Read common vars from common.vars # the incantation here ensures (by env) that only key=value pairs are present # then declare-ing the result puts those vars in our environment declare $(env -i `cat common.vars`) 

De -i zorgt ervoor dat u alleen de variabelen krijgt van common.vars

Update: een illustratie van de beveiliging is dat

env -i "touch evil1 foo=omg boo=$(touch evil2)" 

Geen aangeraakte bestanden zal produceren. Getest op mac met bash, dwz met behulp van bsd env.

Reacties

  • Kijk maar eens hoe evil1 en evil2 bestanden worden gemaakt als je dit in comm on.vars “ touch evil1 foo = omg boo = $ (touch evil2)
  • @pihentagy Voor mij produceert het volgende geen aangeraakte bestanden env -i 'touch evil1 foo=omg boo=$(touch evil2)' . Draait op mac.
  • inderdaad, maar heeft geen toegang tot foo. Ik ‘ heb env -i ... myscript.sh geprobeerd en binnen dat script is foo niet gedefinieerd. Als u echter ” garbage ” verwijdert, zal het werken. Dus bedankt voor het uitleggen. : +1:

Answer

De meeste gebruikers, hoewel niet veel containers, hebben al de git binair. Gebruik daarom git config voor applicatieconfiguratiebeheer met behulp van een speciaal niet-conflicterend configuratiebestand zoals in de onderstaande voorbeelden?

# Set $ git config -f ~/.myapp core.mykey myval # Get $ git config -f ~/.myapp core.mykey myval # Get invalid $ git config -f ~/.myapp core.mykey $ echo $? 1 # List git config -f ~/.myapp -l core.mykey=myval # View $ cat ~/.myapp [core] mykey = myval 

Voor aanvullende commandos, zie de man bladzijde. Het is verstandig om er eerst voor te zorgen dat het configuratiebestand bestaat:

touch -a ~/.myapp 

Antwoord

Deze lijkt veilig en kort. Voel je vrij om dit meedogenloos te kraken. Ik “zou graag een betere manier willen weten.

TL; DR;

while read LINE; do declare "$LINE"; done < evil.conf 

Ik gebruik bash 4.3.48.

Het is ook compatibel met bash --posix. Zie onderaan voor test.

Maar sh ondersteunt het niet vanwege declare.


Basistest voor degenen die bewijs willen

Maak een bestand evil.conf

 echo > evil.conf " A=1 B=2 C=$(echo hello) # Could produce side-effect D=`touch evil` C=$((1+2)) E=$(ping 8.8.8.8 -n 3) echo hello # Could produce visible side-effect touch evil2 ping 8.8.8.8 -n 3 F=ok"  

Laad de configuratie met het fragment

 while read LINE; do declare "$LINE"; done < evil.conf  

Uitvoer (zie sanitizer in actie)

bash: declare: `": not a valid identifier bash: declare: `": not a valid identifier bash: declare: `# Could produce side-effect": not a valid identifier bash: declare: `echo hello": not a valid identifier bash: declare: `": not a valid identifier bash: declare: `# Could produce visible side-effect": not a valid identifier bash: declare: `touch evil2": not a valid identifier bash: declare: `ping 8.8.8.8 -n 3": not a valid identifier 

Laten we nu de waarden controleren

 for V in A B C D E F; do declare -p $V; done  
declare -- A="1" declare -- B="2" declare -- C="\$((1+2))" declare -- D="\`touch evil\`" declare -- E="\$(ping 8.8.8.8 -n 3)" declare -- F="ok" 

Controleer bijwerkingen (geen bijwerkingen):

 ls evil evil2  
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory 

Bijlage. Test bash --posix

bash -c "while read LINE; do declare "$LINE"; done < evil.conf; for V in A B C D E F; do declare -p $V; done" --posix 

Antwoord

Voor mijn scenario source of . w prima, maar ik wilde lokale omgevingsvariabelen ondersteunen (dwz FOO=bar myscript.sh) die voorrang hebben op geconfigureerde variabelen. Ik wilde ook dat het configuratiebestand door de gebruiker bewerkbaar en comfortabel was voor iemand die gewend was configuratiebestanden te vinden, en om het zo klein / eenvoudig mogelijk te houden, om niet af te leiden van de hoofdlijnen van mijn zeer kleine script.

Dit is wat ik bedacht:

CONF=${XDG_CONFIG_HOME:-~/config}/myscript.sh if [ ! -f $CONF ]; then cat > $CONF << CONF VAR1="default value" CONF fi . <(sed "s/^\([^=]\+\) *= *\(.*\)$/\1=${\1:-\2}/" < $CONF) 

In wezen – het controleert op variabele definities (zonder erg flexibel te zijn over witruimte) en herschrijft die regels zodat de waarde wordt geconverteerd naar een standaardwaarde voor die variabele, en de variabele is ongewijzigd als deze wordt gevonden, zoals de XDG_CONFIG_HOME variabele hierboven. Het haalt deze gewijzigde versie van het configuratiebestand op en gaat verder.

Toekomstig werk zou het sed script robuuster kunnen maken, regels eruit filteren die er vreemd uitzien of niet ” t definities, enz., breken niet bij opmerkingen aan het einde van de regel – maar dit is voorlopig goed genoeg voor mij.

Antwoord

U kunt het doen:

#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF 

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *