0

I have a config file called .env which contains:

DB_PASSWORD={password}

Within /root/.mysql/db00.yml.db we have a password for the MySQL root account. I'm trying to pipe this into sed along the lines of:

cat /root/.mysql/db00.yml.db | sed -i -e "s/DB_PASSWORD=/DB_PASSWORD=(.*)/g" .env

Not having any luck, tried a few variations like:

sed -i "s/DB_PASSWORD=/DB_PASSWORD=$(cat /root/.mysql/db00.yml.db)" .env
mysqlpwd=$(cat /root/.mysql/db00.yml.db)
# Use the variable in sed -i
sed -i "s/DB_PASSWORD=/DB_PASSWORD=$mysqlpwd/g" .env

This produces the error:

root@linuxbox:/var/www/www.example.com# mysqlpwd=$(cat /root/.mysql/db00.yml.db)
root@linuxbox:/var/www/www.example.com# sed -i "s/DB_PASSWORD=/DB_PASSWORD=$mysqlpwd/g" .env
sed: -e expression #1, char 33: unknown option to `s'

Update:

Running it without /g also fails:

root@linuxbox:/var/www/www.example.com# echo $mysqlpwd
Rjcr/Sn+s/a2QbGx
root@linuxbox:/var/www/www.example.com# cat .env | grep DB_PASSWORD
DB_PASSWORD=(.*)()
root@linuxbox:/var/www/www.example.com# sed -i "s/DB_PASSWORD=/DB_PASSWORD=$mysqlpwd/" .env
sed: -e expression #1, char 33: unknown option to `s'

None of these approaches seem to work.

I am running sed 4.7, i.e.:

root@linuxbox:/var/www/www.example.com# sed --version
sed (GNU sed) 4.7
13
  • Try putting a / after the pwd in your last example.
    – Mr R
    Mar 19, 2021 at 7:45
  • Assuming this is linux ... if it's OSX you need to add a suffix after the -i.
    – Mr R
    Mar 19, 2021 at 7:46
  • Yep, Debian 10.8. @Mr R: good pick up but the latter with /g seems to fail also. Mar 19, 2021 at 7:48
  • 1
    @JamesSpittal if still having problems - confirm you actually have a value in mysqlpwd .. ie. echo $mysqlpwd
    – Mr R
    Mar 19, 2021 at 7:53
  • 1
    @jamesSpittal sorry - I missed the s off the start of the command ... so sed -i "s|DB_PASSWORD=|DB_PASSWORD=$mysqlpwd|" .env. NOTE: you need to be sure your password doesn't have | - you can use any other character (other than " :-)
    – Mr R
    Mar 19, 2021 at 8:02

4 Answers 4

2

Passwords, by their very nature, must be allowed to contain any character that can be reliably produced by a keyboard. This means that passwords can easily contain valid sed code, or strings that, when injected into a sed editing script, breaks that script, writes to arbitrary files on the system, or, with GNU sed, execute arbitrary commands (extra interesting when you're running as root, which you appear to be).

Don't use sed when you need to use user-supplied data, unless you know exactly how to sanitize it. I would personally avoid sed for anything that would require me to double quote the sed expression (instead of single-quote it) to allow the shell to expand a variable that I had little control over.

Instead, use awk:

awk -F '=' '
    BEGIN { OFS = FS }
    NR == FNR { password = $0; next }
    $1 == "DB_PASSWORD" { sub("=.*",""); $2 = password }; 1' /root/.mysql/db00.yml.db .env >.env.new &&
mv .env.new .env

This first reads the password from the first line of the first file (it's assumed that this only contains a single line with the verbatim password).

It then treats your .env file as a file of =-delimited fields.

When the first field on a line is DB_PASSWORD, it clears everything after the first = on the line (just in case the old password contains =), and inserts the password in as the second field.

All lines, whether modified or not, are outputted to .env.new, and this file then replaces the old .env with mv.

Crucially, I never use the data read from either of the two files as code in my awk program, and I never allow awk to treat any part of the password as special. Note that using the password as the replacement string in a call to sub() would be problematic as this would treat any & in the string as special.

1

Try using a different delimiter for sed ... There are still risks of it failing depending on your actual password (e.g. if it included | or & or \1).

sed -i "s|DB_PASSWORD=|DB_PASSWORD=$mysqlpwd|" .env
6
  • 1
    And you wouldn't do this if the args to sed show up in ps, or sed was going to take a long time to process (because .env was massive and the disk was slow...)
    – Mr R
    Mar 19, 2021 at 8:08
  • I do not follow, sorry. Could you elaborate? Mar 19, 2021 at 8:12
  • With the correct arguments to ps you can see everything passed on the command line .. in this case with the password ending up on the query string it is a risky thing - albeit because it happens so fast unlikely to be a consequential risk.
    – Mr R
    Mar 19, 2021 at 8:17
  • 1
    This would not work with a password containing |.
    – Kusalananda
    Mar 19, 2021 at 8:18
  • 1
    Correct - nor would it work with \1 or & either .. that is mentioned in the discussion on the question ..
    – Mr R
    Mar 19, 2021 at 8:18
0

You should not put passwords in command line arguments; they can be read by any user on the system via ps or top or many other ways. I understand that may not be likely in some automation scenarios (because no one is likely to be logged into the box) but it's still a bad idea, and there's always the possibility that some monitoring tool or some script by someone else will happen to log the processes running on the system at some point in time—thereby logging your password to who knows where.

A good answer was already given using awk; I also wrote a simple solution using ex which I'll share. This will work for any password with any special characters with the sole exception that it can't end in a backslash.

If file .env contains:

somevar1=someval
somevar2=some other val
DB_PASSWORD=
somevar3=another val
somevar4=val

And myscript contains:

/^DB_PASSWORD=$/a
!@#$#% some complicated password "^$%^&@#,.,cmxz\k'dvok][
.
0//j!
x

Just run ex .env < myscript to get results as shown:

$ ex .env < myscript
$ cat .env
somevar1=someval
somevar2=some other val
DB_PASSWORD=!@#$#% some complicated password "^$%^&@#,.,cmxz\k'dvok][
somevar3=another val
somevar4=val
$ 
0
0
sed -i -e "
  /^DB_PASSWORD=/c\\
DB_PASSWORD=$(sed -e 's:[\\]:&&:g' /root/.mysql/db00.yml.db)
"  ./.env
  • We integrate within the sed code itself the sanitising of the password for plug worthiness in the change command c\

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .