shlinkio / shlink

The definitive self-hosted URL shortener
https://shlink.io
MIT License
3.21k stars 257 forks source link

Numeric env vars are casted to int, failing if a string is expected #2058

Closed MrDrache333 closed 7 months ago

MrDrache333 commented 7 months ago

Shlink version

4.0.2

PHP version

8.3.0

How do you serve Shlink

Docker image

Database engine

MySQL

Database version

8.3.0

Current behavior

Everything worked fine for weeks until I upgraded the mysql Database Image from 8.0 to the current Version 8.3. The Database starts up normally but the normal Shlink instance didn't. The following log shows the last outputs from the Shlink Container after startup with the verbosity env set to 3.

Initializing database if needed... [Running "/usr/local/bin/php bin/cli db:create"]   RUN  '/usr/local/bin/php' 'bin/cli' 'db:create'
  OUT  
  OUT  Fatal error: Uncaught TypeError: PDO::__construct(): Argument #3 ($password) must be of type ?string, int given in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php:33
  OUT  Stack trace:
  OUT  #0 /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php(33): PDO->__construct('mysql:host=shli...', 'shlink', Object(SensitiveParameterValue), Array)
  OUT  #1 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(217): Doctrine\DBAL\Driver\PDO\MySQL\Driver->connect(Object(SensitiveParameterValue))
  OUT  #2 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(236): Doctrine\DBAL\Connection->connect()
  OUT  #3 /etc/shlink/vendor/doctrine/dbal/src/Driver/AbstractMySQLDriver.php(35): Doctrine\DBAL\Connection->getServerVersion()
  OUT  #4 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(191): Doctrine\DBAL\Driver\AbstractMySQLDriver->getDatabasePlatform(Object(Doctrine\DBAL\Connection))
  OUT  #5 /etc/shlink/vendor/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php(18): Doctrine\DBAL\Connection->getDatabasePlatform()
  OUT  #6 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(1154): Doctrine\DBAL\Schema\DefaultSchemaManagerFactory->createSchemaManager(Object(Doctrine\DBAL\Connection))
  OUT  #7 /etc/shlink/module/CLI/src/Command/Db/CreateDatabaseCommand.php(91): Doctrine\DBAL\Connection->createSchemaManager()
  OUT  #8 /etc/shlink/module/CLI/src/Command/Db/CreateDatabaseCommand.php(71): Shlinkio\Shlink\CLI\Command\Db\CreateDatabaseCommand->ensureDatabaseExistsAndGetTables()
  OUT  #9 /etc/shlink/module/CLI/src/Command/Db/CreateDatabaseCommand.php(56): Shlinkio\Shlink\CLI\Command\Db\CreateDatabaseCommand->databaseTablesExist()
  OUT  #10 /etc/shlink/module/CLI/src/Command/Util/AbstractLockedCommand.php(35): Shlinkio\Shlink\CLI\Command\Db\CreateDatabaseCommand->lockedExecute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #11 /etc/shlink/vendor/symfony/console/Command/Command.php(279): Shlinkio\Shlink\CLI\Command\Util\AbstractLockedCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #12 /etc/shlink/vendor/symfony/console/Application.php(1031): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #13 /etc/shlink/vendor/symfony/console/Application.php(318): Symfony\Component\Console\Application->doRunCommand(Object(Shlinkio\Shlink\CLI\Command\Db\CreateDatabaseCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #14 /etc/shlink/vendor/symfony/console/Application.php(169): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #15 /etc/shlink/bin/cli(10): Symfony\Component\Console\Application->run()
  OUT  #16 {main}
  OUT    thrown in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php on line 33
  OUT  
  RES  255 Command did not run successfully
Updating database... [Running "/usr/local/bin/php bin/cli db:migrate"]   RUN  '/usr/local/bin/php' 'bin/cli' 'db:migrate'
  OUT  Migrating database...
  OUT  
  ERR    RUN  '/usr/local/bin/php' 'vendor/doctrine/migrations/bin/doctrine-migrations.php' 'migrations:migrate' '--no-interaction'
  ERR    OUT  
  ERR    OUT  Fatal error: Uncaught TypeError: PDO::__construct(): Argument #3 ($password) must be of type ?string, int given in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php:33
  ERR    OUT  Stack trace:
  ERR    OUT  #0 /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php(33): PDO->__construct('mysql:host=shli...', 'shlink', Object(SensitiveParameterValue), Array)
  ERR    OUT  #1 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(217): Doctrine\DBAL\Driver\PDO\MySQL\Driver->connect(Object(SensitiveParameterValue))
  ERR    OUT  #2 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(236): Doctrine\DBAL\Connection->connect()
  ERR    OUT  #3 /etc/shlink/vendor/doctrine/dbal/src/Driver/AbstractMySQLDriver.php(35): Doctrine\DBAL\Connection->getServerVersion()
  ERR    OUT  #4 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(191): Doctrine\DBAL\Driver\AbstractMySQLDriver->getDatabasePlatform(Object(Doctrine\DBAL\Connection))
  ERR    OUT  #5 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(150): Doctrine\DBAL\Connection->getDatabasePlatform()
  ERR    OUT  #6 /etc/shlink/vendor/doctrine/migrations/lib/Doctrine/Migrations/Tools/Console/Command/MigrateCommand.php(141): Doctrine\DBAL\Connection->getDatabase()
  ERR    OUT  #7 /etc/shlink/vendor/symfony/console/Command/Command.php(279): Doctrine\Migrations\Tools\Console\Command\MigrateCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ERR    OUT  #8 /etc/shlink/vendor/symfony/console/Application.php(1031): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ERR    OUT  #9 /etc/shlink/vendor/symfony/console/Application.php(318): Symfony\Component\Console\Application->doRunCommand(Object(Doctrine\Migrations\Tools\Console\Command\MigrateCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ERR    OUT  #10 /etc/shlink/vendor/symfony/console/Application.php(169): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ERR    OUT  #11 /etc/shlink/vendor/doctrine/migrations/lib/Doctrine/Migrations/Tools/Console/ConsoleRunner.php(95): Symfony\Component\Console\Application->run()
  ERR    OUT  #12 /etc/shlink/vendor/doctrine/migrations/bin/doctrine-migrations.php(45): Doctrine\Migrations\Tools\Console\ConsoleRunner::run(Array, Object(Doctrine\Migrations\DependencyFactory))
  ERR    OUT  #13 /etc/shlink/vendor/doctrine/migrations/bin/doctrine-migrations.php(46): Doctrine\Migrations\{closure}()
  ERR    OUT  #14 {main}
  ERR    OUT    thrown in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php on line 33
  ERR    OUT  
  ERR  In Process.php line 267:
  ERR                                                                                 
  ERR    [Symfony\Component\Process\Exception\ProcessFailedException]                 
  ERR    The command "'/usr/local/bin/php' 'vendor/doctrine/migrations/bin/doctrine-  
  ERR    migrations.php' 'migrations:migrate' '--no-interaction'" failed.             
  ERR                                                                                 
  ERR    Exit Code: 255(Unknown error)                                                
  ERR                                                                                 
  ERR    Working directory: /etc/shlink                                               
  ERR                                                                                 
  ERR    Output:                                                                      
  ERR    ================                                                             
  ERR                                                                                 
  ERR    Fatal error: Uncaught TypeError: PDO::__construct(): Argument #3 ($password  
  ERR    ) must be of type ?string, int given in /etc/shlink/vendor/doctrine/dbal/sr  
  ERR    c/Driver/PDO/MySQL/Driver.php:33                                             
  ERR    Stack trace:                                                                 
  ERR    #0 /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php(33): PD  
  ERR    O->__construct('mysql:host=shli...', 'shlink', Object(SensitiveParameterVal  
  ERR    ue), Array)                                                                  
  ERR    #1 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(217): Doctrine\DBAL\  
  ERR    Driver\PDO\MySQL\Driver->connect(Object(SensitiveParameterValue))            
  ERR    #2 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(236): Doctrine\DBAL\  
  ERR    Connection->connect()                                                        
  ERR    #3 /etc/shlink/vendor/doctrine/dbal/src/Driver/AbstractMySQLDriver.php(35):  
  ERR     Doctrine\DBAL\Connection->getServerVersion()                                
  ERR    #4 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(191): Doctrine\DBAL\  
  ERR    Driver\AbstractMySQLDriver->getDatabasePlatform(Object(Doctrine\DBAL\Connec  
  ERR    tion))                                                                       
  ERR    #5 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(150): Doctrine\DBAL\  
  ERR    Connection->getDatabasePlatform()                                            
  ERR    #6 /etc/shlink/vendor/doctrine/migrations/lib/Doctrine/Migrations/Tools/Con  
  ERR    sole/Command/MigrateCommand.php(141): Doctrine\DBAL\Connection->getDatabase  
  ERR    ()                                                                           
  ERR    #7 /etc/shlink/vendor/symfony/console/Command/Command.php(279): Doctrine\Mi  
  ERR    grations\Tools\Console\Command\MigrateCommand->execute(Object(Symfony\Compo  
  ERR    nent\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\Cons  
  ERR    oleOutput))                                                                  
  ERR    #8 /etc/shlink/vendor/symfony/console/Application.php(1031): Symfony\Compon  
  ERR    ent\Console\Command\Command->run(Object(Symfony\Component\Console\Input\Arg  
  ERR    vInput), Object(Symfony\Component\Console\Output\ConsoleOutput))             
  ERR    #9 /etc/shlink/vendor/symfony/console/Application.php(318): Symfony\Compone  
  ERR    nt\Console\Application->doRunCommand(Object(Doctrine\Migrations\Tools\Conso  
  ERR    le\Command\MigrateCommand), Object(Symfony\Component\Console\Input\ArgvInpu  
  ERR    t), Object(Symfony\Component\Console\Output\ConsoleOutput))                  
  ERR    #10 /etc/shlink/vendor/symfony/console/Application.php(169): Symfony\Compon  
  ERR    ent\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvI  
  ERR    nput), Object(Symfony\Component\Console\Output\ConsoleOutput))               
  ERR    #11 /etc/shlink/vendor/doctrine/migrations/lib/Doctrine/Migrations/Tools/Co  
  ERR    nsole/ConsoleRunner.php(95): Symfony\Component\Console\Application->run()    
  ERR    #12 /etc/shlink/vendor/doctrine/migrations/bin/doctrine-migrations.php(45):  
  ERR     Doctrine\Migrations\Tools\Console\ConsoleRunner::run(Array, Object(Doctrin  
  ERR    e\Migrations\DependencyFactory))                                             
  ERR    #13 /etc/shlink/vendor/doctrine/migrations/bin/doctrine-migrations.php(46):  
  ERR     Doctrine\Migrations\{closure}()                                             
  ERR    #14 {main}                                                                   
  ERR      thrown in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.ph  
  ERR    p on line 33                                                                 
  ERR                                                                                 
  ERR                                                                                 
  ERR    Error Output:                                                                
  ERR    ================                                                             
  ERR                                                                                 
  ERR  
  ERR  Exception trace:
  ERR    at /etc/shlink/vendor/symfony/process/Process.php:267
  ERR   Symfony\Component\Process\Process->mustRun() at /etc/shlink/module/CLI/src/Util/ProcessRunner.php:48
  ERR   Shlinkio\Shlink\CLI\Util\ProcessRunner->run() at /etc/shlink/module/CLI/src/Command/Db/AbstractDatabaseCommand.php:30
  ERR   Shlinkio\Shlink\CLI\Command\Db\AbstractDatabaseCommand->runPhpCommand() at /etc/shlink/module/CLI/src/Command/Db/MigrateDatabaseCommand.php:31
  ERR   Shlinkio\Shlink\CLI\Command\Db\MigrateDatabaseCommand->lockedExecute() at /etc/shlink/module/CLI/src/Command/Util/AbstractLockedCommand.php:35
  ERR   Shlinkio\Shlink\CLI\Command\Util\AbstractLockedCommand->execute() at /etc/shlink/vendor/symfony/console/Command/Command.php:279
  ERR   Symfony\Component\Console\Command\Command->run() at /etc/shlink/vendor/symfony/console/Application.php:1031
  ERR   Symfony\Component\Console\Application->doRunCommand() at /etc/shlink/vendor/symfony/console/Application.php:318
  ERR   Symfony\Component\Console\Application->doRun() at /etc/shlink/vendor/symfony/console/Application.php:169
  ERR   Symfony\Component\Console\Application->run() at /etc/shlink/bin/cli:10
  ERR  
  ERR  db:migrate
  ERR  
  ERR  
  RES  1 Command did not run successfully
Generating proxies... [Running "/usr/local/bin/php bin/doctrine orm:generate-proxies"]   RUN  '/usr/local/bin/php' 'bin/doctrine' 'orm:generate-proxies'
  OUT  
  OUT  Fatal error: Uncaught TypeError: PDO::__construct(): Argument #3 ($password) must be of type ?string, int given in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php:33
  OUT  Stack trace:
  OUT  #0 /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php(33): PDO->__construct('mysql:host=shli...', 'shlink', Object(SensitiveParameterValue), Array)
  OUT  #1 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(217): Doctrine\DBAL\Driver\PDO\MySQL\Driver->connect(Object(SensitiveParameterValue))
  OUT  #2 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(236): Doctrine\DBAL\Connection->connect()
  OUT  #3 /etc/shlink/vendor/doctrine/dbal/src/Driver/AbstractMySQLDriver.php(35): Doctrine\DBAL\Connection->getServerVersion()
  OUT  #4 /etc/shlink/vendor/doctrine/dbal/src/Connection.php(191): Doctrine\DBAL\Driver\AbstractMySQLDriver->getDatabasePlatform(Object(Doctrine\DBAL\Connection))
  OUT  #5 /etc/shlink/vendor/doctrine/orm/src/Mapping/ClassMetadataFactory.php(724): Doctrine\DBAL\Connection->getDatabasePlatform()
  OUT  #6 /etc/shlink/vendor/doctrine/orm/src/Mapping/ClassMetadataFactory.php(554): Doctrine\ORM\Mapping\ClassMetadataFactory->getTargetPlatform()
  OUT  #7 /etc/shlink/vendor/doctrine/orm/src/Mapping/ClassMetadataFactory.php(174): Doctrine\ORM\Mapping\ClassMetadataFactory->completeIdGeneratorMapping(Object(Doctrine\ORM\Mapping\ClassMetadata))
  OUT  #8 /etc/shlink/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(343): Doctrine\ORM\Mapping\ClassMetadataFactory->doLoadMetadata(Object(Doctrine\ORM\Mapping\ClassMetadata), NULL, false, Array)
  OUT  #9 /etc/shlink/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(207): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata('Shlinkio\\Shlink...')
  OUT  #10 /etc/shlink/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(96): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor('Shlinkio\\Shlink...')
  OUT  #11 /etc/shlink/vendor/doctrine/orm/src/Tools/Console/Command/GenerateProxiesCommand.php(46): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getAllMetadata()
  OUT  #12 /etc/shlink/vendor/symfony/console/Command/Command.php(279): Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #13 /etc/shlink/vendor/symfony/console/Application.php(1031): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #14 /etc/shlink/vendor/symfony/console/Application.php(318): Symfony\Component\Console\Application->doRunCommand(Object(Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #15 /etc/shlink/vendor/symfony/console/Application.php(169): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  OUT  #16 /etc/shlink/vendor/doctrine/orm/src/Tools/Console/ConsoleRunner.php(30): Symfony\Component\Console\Application->run()
  OUT  #17 /etc/shlink/bin/doctrine(12): Doctrine\ORM\Tools\Console\ConsoleRunner::run(Object(Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider))
  OUT  #18 {main}
  OUT    thrown in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php on line 33
  OUT  
  RES  255 Command did not run successfully
Clearing entities cache... [Running "/usr/local/bin/php bin/doctrine orm:clear-cache:metadata"]   RUN  '/usr/local/bin/php' 'bin/doctrine' 'orm:clear-cache:metadata'
  ERR  
  ERR   // Clearing all Metadata cache entries                                         
  ERR  
  ERR   [OK] Successfully deleted cache entries.                                       
  ERR  
  ERR  
  RES  Command ran successfully

!
Downloading GeoLite2 db file... [Running "/usr/local/bin/php bin/cli visit:download-db"]   RUN  '/usr/local/bin/php' 'bin/cli' 'visit:download-db'
  OUT  
  OUT   [INFO] GeoLite2 db file is up to date.                                         
  OUT  
  OUT  
  RES  Command ran successfully


Expected behavior

A normal startup just like before.

Minimum steps to reproduce

version: '3'
services:
    shlink-web-client:
        container_name: shlink-web-client
        image: "shlinkio/shlink-web-client"
        restart: always
        ports:
            - 7002:8080
        networks:
          - 'shlink'
    shlink:
        container_name: shlink_OG
        depends_on:
          - shlink_db
        image: "shlinkio/shlink"
        restart: always
        ports:
            - 7000:8080
        environment:
            GEOLITE_LICENSE_KEY: SOMEKEY
            USE_HTTPS: true
            DEFAULT_DOMAIN: mydomain.de
            DB_DRIVER: mysql
            DB_HOST: shlink_db
            DB_USER: shlink
            DB_PASSWORD: "1234"
            SHLINK_PROMETHEUS_EXPORTER_ENABLED: 1
            SHLINK_PROMETHEUS_EXPORTER_BIND_ADDRESS: :9090
            SHELL_VERBOSITY: 3
        links:
            - shlink_db
        networks:
          - 'shlink'
    shlink_db:
        container_name: shlink_db
        restart: always
        image: mysql:8.3-oracle
        environment:
            MYSQL_DATABASE: shlink
            MYSQL_USER: shlink
            MYSQL_PASSWORD: "1234"
            MYSQL_ROOT_PASSWORD: "1234"
        volumes:
            - shlinkdb:/var/lib/mysql
        networks:
          - 'shlink'
networks:
  shlink:

volumes:
  shlinkdb:
acelaya commented 7 months ago

That's not related to MySQL's version, but with the fact that your password is in fact a number (1234).

See https://github.com/shlinkio/shlink-config/blob/main/functions/functions.php#L43

I need to re-evaluate this decision, but it should work if you use a non-fully-numeric password.

acelaya commented 7 months ago

BTW, this is probably a bug introduced in Shlink 4.0.0. One thing it changes is updating to the latest major version of the database abstraction library.

I suppose previous versions didn't run in strict mode, which makes integers be converted to string transparently. In strict mode it would throw an error like this instead.

It's usually a good practice to define strict versions for images, as in this case you got Shlink unintentionally updated when updating mysql, making you think that was the problem.

MrDrache333 commented 7 months ago

BTW, this is probably a bug introduced in Shlink 4.0.0. One thing it changes is updating to the latest major version of the database abstraction library.

I suppose previous versions didn't run in strict mode, which makes integers be converted to string transparently. In strict mode it would throw an error like this instead.

It's usually a good practice to define strict versions for images, as in this case you got Shlink unintentionally updated when updating mysql, making you think that was the problem.

I can confirm this. Although the password provided in my compose doesn't show the real configured password, it actually contained just numbers. Because the Database was already created, changing the password afterwards resulted in a failed authentication. Changing the password back to the previous one with just numbers and rolling back to shlink Version 3 resolved the error. Thanks for the explanation.

acelaya commented 7 months ago

I'll keep this open, as I actually need to fix it 😅. If you want (or have to) use only numbers as the database password, it should be possible.

BTW, rolling back from Shlink 4.x to 3.x might not be a great idea, as it has applied a bunch of database migrations that have left the database in an incompatible way with Shlink 3.x. You'll run into issues, probably when trying to create a new short URL, and definitely in case you are using device-specific long urls.

acelaya commented 7 months ago

I have just released Shlink 4.0.3, which fixes this. You should be able to use numeric database credentials for the database with that version.