├── .github └── workflows │ ├── codespell.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .rubocop.yml ├── CHANGELOG.md ├── Gemfile ├── README.md ├── Rakefile ├── controls ├── mysql_conf.rb └── mysql_db.rb ├── inspec.yml ├── libraries ├── mysql_distribution.rb └── mysql_version.rb └── renovate.json /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Codespell - Spellcheck 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: [master] 7 | pull_request: 8 | branches: [master] 9 | 10 | jobs: 11 | codespell: 12 | uses: "dev-sec/.github/.github/workflows/codespell.yml@main" 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: New release 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | generate_changelog: 12 | uses: dev-sec/.github/.github/workflows/baseline-release.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | schedule: 9 | - cron: '0 6 * * *' 10 | 11 | jobs: 12 | test: 13 | uses: dev-sec/.github/.github/workflows/baseline-test.yml@main 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.librarian 2 | **/Puppetfile.lock 3 | **/.tmp 4 | Gemfile.lock 5 | Berksfile.lock 6 | inspec.lock 7 | nbproject 8 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AllCops: 3 | Exclude: 4 | - vendor/**/* 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [5.1.0](https://github.com/dev-sec/mysql-baseline/tree/5.1.0) (2023-11-12) 4 | 5 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/5.0.0...5.1.0) 6 | 7 | **Implemented enhancements:** 8 | 9 | - add filter for MySQL roles [\#82](https://github.com/dev-sec/mysql-baseline/pull/82) ([schurzi](https://github.com/schurzi)) 10 | 11 | **Merged pull requests:** 12 | 13 | - add spellchecking with codespell [\#81](https://github.com/dev-sec/mysql-baseline/pull/81) ([schurzi](https://github.com/schurzi)) 14 | - Configure Renovate [\#80](https://github.com/dev-sec/mysql-baseline/pull/80) ([renovate[bot]](https://github.com/apps/renovate)) 15 | 16 | ## [5.0.0](https://github.com/dev-sec/mysql-baseline/tree/5.0.0) (2022-12-19) 17 | 18 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/4.1.0...5.0.0) 19 | 20 | **Implemented enhancements:** 21 | 22 | - use centralised issue templates and workflows [\#79](https://github.com/dev-sec/mysql-baseline/pull/79) ([schurzi](https://github.com/schurzi)) 23 | - support for el9 [\#76](https://github.com/dev-sec/mysql-baseline/pull/76) ([rndmh3ro](https://github.com/rndmh3ro)) 24 | 25 | **Merged pull requests:** 26 | 27 | - Delete first control [\#78](https://github.com/dev-sec/mysql-baseline/pull/78) ([rndmh3ro](https://github.com/rndmh3ro)) 28 | - add suse support [\#77](https://github.com/dev-sec/mysql-baseline/pull/77) ([rndmh3ro](https://github.com/rndmh3ro)) 29 | 30 | ## [4.1.0](https://github.com/dev-sec/mysql-baseline/tree/4.1.0) (2022-08-16) 31 | 32 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/4.0.4...4.1.0) 33 | 34 | **Merged pull requests:** 35 | 36 | - fix wrong process name [\#75](https://github.com/dev-sec/mysql-baseline/pull/75) ([rndmh3ro](https://github.com/rndmh3ro)) 37 | - debian 11 has a mysql@localhost user with all privs [\#74](https://github.com/dev-sec/mysql-baseline/pull/74) ([rndmh3ro](https://github.com/rndmh3ro)) 38 | - process name and the service name differ on debian 11 [\#73](https://github.com/dev-sec/mysql-baseline/pull/73) ([rndmh3ro](https://github.com/rndmh3ro)) 39 | - do not run logfile check if no logfile is specified [\#72](https://github.com/dev-sec/mysql-baseline/pull/72) ([rndmh3ro](https://github.com/rndmh3ro)) 40 | - Change linting to Cookstyle [\#71](https://github.com/dev-sec/mysql-baseline/pull/71) ([schurzi](https://github.com/schurzi)) 41 | 42 | ## [4.0.4](https://github.com/dev-sec/mysql-baseline/tree/4.0.4) (2022-01-12) 43 | 44 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/4.0.3...4.0.4) 45 | 46 | **Implemented enhancements:** 47 | 48 | - use service name for process check instead of hardcoding it [\#65](https://github.com/dev-sec/mysql-baseline/pull/65) ([rndmh3ro](https://github.com/rndmh3ro)) 49 | 50 | **Fixed bugs:** 51 | 52 | - fix service process control check [\#67](https://github.com/dev-sec/mysql-baseline/pull/67) ([rndmh3ro](https://github.com/rndmh3ro)) 53 | 54 | **Closed issues:** 55 | 56 | - secure-auth is deprecated in MySQL 8.0.3 [\#58](https://github.com/dev-sec/mysql-baseline/issues/58) 57 | 58 | **Merged pull requests:** 59 | 60 | - use input instead of attribute [\#70](https://github.com/dev-sec/mysql-baseline/pull/70) ([micheelengronne](https://github.com/micheelengronne)) 61 | - format and update README.md [\#69](https://github.com/dev-sec/mysql-baseline/pull/69) ([schurzi](https://github.com/schurzi)) 62 | - add dependency to chef-config for CI [\#66](https://github.com/dev-sec/mysql-baseline/pull/66) ([schurzi](https://github.com/schurzi)) 63 | - use version tag for changelog action [\#64](https://github.com/dev-sec/mysql-baseline/pull/64) ([schurzi](https://github.com/schurzi)) 64 | - Fix lint [\#63](https://github.com/dev-sec/mysql-baseline/pull/63) ([schurzi](https://github.com/schurzi)) 65 | - add github action for testing [\#62](https://github.com/dev-sec/mysql-baseline/pull/62) ([rndmh3ro](https://github.com/rndmh3ro)) 66 | 67 | ## [4.0.3](https://github.com/dev-sec/mysql-baseline/tree/4.0.3) (2020-10-20) 68 | 69 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/4.0.2...4.0.3) 70 | 71 | **Fixed bugs:** 72 | 73 | - fix quotes in library again [\#61](https://github.com/dev-sec/mysql-baseline/pull/61) ([rndmh3ro](https://github.com/rndmh3ro)) 74 | 75 | ## [4.0.2](https://github.com/dev-sec/mysql-baseline/tree/4.0.2) (2020-10-20) 76 | 77 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/4.0.1...4.0.2) 78 | 79 | **Closed issues:** 80 | 81 | - Dynamically determine logfile path [\#53](https://github.com/dev-sec/mysql-baseline/issues/53) 82 | - mysql 5.7.6 password -\> authentication\_string [\#35](https://github.com/dev-sec/mysql-baseline/issues/35) 83 | 84 | **Merged pull requests:** 85 | 86 | - fix mysql\_version command [\#60](https://github.com/dev-sec/mysql-baseline/pull/60) ([rndmh3ro](https://github.com/rndmh3ro)) 87 | - use custom resource to get mysql version and distribution [\#59](https://github.com/dev-sec/mysql-baseline/pull/59) ([rndmh3ro](https://github.com/rndmh3ro)) 88 | - check if a password column exists and only then check contents [\#57](https://github.com/dev-sec/mysql-baseline/pull/57) ([rndmh3ro](https://github.com/rndmh3ro)) 89 | 90 | ## [4.0.1](https://github.com/dev-sec/mysql-baseline/tree/4.0.1) (2020-08-28) 91 | 92 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/4.0.0...4.0.1) 93 | 94 | **Merged pull requests:** 95 | 96 | - only run password-checks when the appropriate columns exist [\#52](https://github.com/dev-sec/mysql-baseline/pull/52) ([rndmh3ro](https://github.com/rndmh3ro)) 97 | 98 | ## [4.0.0](https://github.com/dev-sec/mysql-baseline/tree/4.0.0) (2020-08-26) 99 | 100 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.1.7...4.0.0) 101 | 102 | **Merged pull requests:** 103 | 104 | - BREAKING: config-files should be owned by mysql-user [\#56](https://github.com/dev-sec/mysql-baseline/pull/56) ([rndmh3ro](https://github.com/rndmh3ro)) 105 | 106 | ## [3.1.7](https://github.com/dev-sec/mysql-baseline/tree/3.1.7) (2020-08-17) 107 | 108 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.1.6...3.1.7) 109 | 110 | **Fixed bugs:** 111 | 112 | - fix wrong parameter in new dynamic determination [\#55](https://github.com/dev-sec/mysql-baseline/pull/55) ([rndmh3ro](https://github.com/rndmh3ro)) 113 | 114 | ## [3.1.6](https://github.com/dev-sec/mysql-baseline/tree/3.1.6) (2020-08-12) 115 | 116 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.1.5...3.1.6) 117 | 118 | **Closed issues:** 119 | 120 | - CentOS 7 and 8 logfile is /var/log/mariadb/mariadb.log [\#50](https://github.com/dev-sec/mysql-baseline/issues/50) 121 | 122 | **Merged pull requests:** 123 | 124 | - dynamically define mysql datadir and log\_error [\#54](https://github.com/dev-sec/mysql-baseline/pull/54) ([rndmh3ro](https://github.com/rndmh3ro)) 125 | 126 | ## [3.1.5](https://github.com/dev-sec/mysql-baseline/tree/3.1.5) (2020-08-05) 127 | 128 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.1.4...3.1.5) 129 | 130 | **Merged pull requests:** 131 | 132 | - change log path and name for centos\>7 [\#51](https://github.com/dev-sec/mysql-baseline/pull/51) ([rndmh3ro](https://github.com/rndmh3ro)) 133 | 134 | ## [3.1.4](https://github.com/dev-sec/mysql-baseline/tree/3.1.4) (2020-07-23) 135 | 136 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.1.3...3.1.4) 137 | 138 | **Merged pull requests:** 139 | 140 | - The release draft references the correct SHA [\#49](https://github.com/dev-sec/mysql-baseline/pull/49) ([micheelengronne](https://github.com/micheelengronne)) 141 | 142 | ## [3.1.3](https://github.com/dev-sec/mysql-baseline/tree/3.1.3) (2020-07-13) 143 | 144 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.1.2...3.1.3) 145 | 146 | **Merged pull requests:** 147 | 148 | - Change default: to value: [\#48](https://github.com/dev-sec/mysql-baseline/pull/48) ([enzomignogna](https://github.com/enzomignogna)) 149 | 150 | ## [3.1.2](https://github.com/dev-sec/mysql-baseline/tree/3.1.2) (2020-06-18) 151 | 152 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.1.1...3.1.2) 153 | 154 | **Merged pull requests:** 155 | 156 | - version alignment [\#47](https://github.com/dev-sec/mysql-baseline/pull/47) ([micheelengronne](https://github.com/micheelengronne)) 157 | 158 | ## [3.1.1](https://github.com/dev-sec/mysql-baseline/tree/3.1.1) (2020-06-18) 159 | 160 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.1.0...3.1.1) 161 | 162 | **Merged pull requests:** 163 | 164 | - github actions release [\#46](https://github.com/dev-sec/mysql-baseline/pull/46) ([micheelengronne](https://github.com/micheelengronne)) 165 | 166 | ## [3.1.0](https://github.com/dev-sec/mysql-baseline/tree/3.1.0) (2019-05-15) 167 | 168 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/3.0.0...3.1.0) 169 | 170 | **Merged pull requests:** 171 | 172 | - Bump version to 3.1.0 and switch to inspec 3 for check [\#45](https://github.com/dev-sec/mysql-baseline/pull/45) ([alexpop](https://github.com/alexpop)) 173 | - Change version string comparison [\#44](https://github.com/dev-sec/mysql-baseline/pull/44) ([rndmh3ro](https://github.com/rndmh3ro)) 174 | - Update issue templates [\#43](https://github.com/dev-sec/mysql-baseline/pull/43) ([artem-sidorenko](https://github.com/artem-sidorenko)) 175 | - update rubocop gem dependency [\#42](https://github.com/dev-sec/mysql-baseline/pull/42) ([chris-rock](https://github.com/chris-rock)) 176 | - add missing impact and title to inspec control [\#41](https://github.com/dev-sec/mysql-baseline/pull/41) ([chris-rock](https://github.com/chris-rock)) 177 | 178 | ## [3.0.0](https://github.com/dev-sec/mysql-baseline/tree/3.0.0) (2018-05-04) 179 | 180 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/2.2.0...3.0.0) 181 | 182 | **Merged pull requests:** 183 | 184 | - use inspec controls [\#40](https://github.com/dev-sec/mysql-baseline/pull/40) ([chris-rock](https://github.com/chris-rock)) 185 | 186 | ## [2.2.0](https://github.com/dev-sec/mysql-baseline/tree/2.2.0) (2018-05-04) 187 | 188 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/2.1.0...2.2.0) 189 | 190 | **Closed issues:** 191 | 192 | - mysql\_log\_path/file should not be checked [\#33](https://github.com/dev-sec/mysql-baseline/issues/33) 193 | 194 | **Merged pull requests:** 195 | 196 | - 2.2.0 [\#39](https://github.com/dev-sec/mysql-baseline/pull/39) ([chris-rock](https://github.com/chris-rock)) 197 | - remove logfile check [\#38](https://github.com/dev-sec/mysql-baseline/pull/38) ([rndmh3ro](https://github.com/rndmh3ro)) 198 | - use recommended spdx license identifier [\#37](https://github.com/dev-sec/mysql-baseline/pull/37) ([chris-rock](https://github.com/chris-rock)) 199 | 200 | ## [2.1.0](https://github.com/dev-sec/mysql-baseline/tree/2.1.0) (2017-05-08) 201 | 202 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/2.0.0...2.1.0) 203 | 204 | **Merged pull requests:** 205 | 206 | - Update metadata [\#36](https://github.com/dev-sec/mysql-baseline/pull/36) ([chris-rock](https://github.com/chris-rock)) 207 | - update centos7 service name [\#34](https://github.com/dev-sec/mysql-baseline/pull/34) ([rndmh3ro](https://github.com/rndmh3ro)) 208 | - restrict ruby testing to version 2.3.3 and update gemfile [\#32](https://github.com/dev-sec/mysql-baseline/pull/32) ([atomic111](https://github.com/atomic111)) 209 | - streamline config owner, remove duplicate [\#31](https://github.com/dev-sec/mysql-baseline/pull/31) ([rndmh3ro](https://github.com/rndmh3ro)) 210 | 211 | ## [2.0.0](https://github.com/dev-sec/mysql-baseline/tree/2.0.0) (2017-01-02) 212 | 213 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/1.2.0...2.0.0) 214 | 215 | **Merged pull requests:** 216 | 217 | - ensure the mysql config file permission is verified [\#30](https://github.com/dev-sec/mysql-baseline/pull/30) ([chris-rock](https://github.com/chris-rock)) 218 | - migrate from Serverspec to InSpec [\#29](https://github.com/dev-sec/mysql-baseline/pull/29) ([chris-rock](https://github.com/chris-rock)) 219 | 220 | ## [1.2.0](https://github.com/dev-sec/mysql-baseline/tree/1.2.0) (2015-10-15) 221 | 222 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/1.1.0...1.2.0) 223 | 224 | ## [1.1.0](https://github.com/dev-sec/mysql-baseline/tree/1.1.0) (2014-09-11) 225 | 226 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/1.0.0...1.1.0) 227 | 228 | **Merged pull requests:** 229 | 230 | - make sure conf files are not writable or executable by others [\#16](https://github.com/dev-sec/mysql-baseline/pull/16) ([arlimus](https://github.com/arlimus)) 231 | - install server and apply hardening in separate steps [\#14](https://github.com/dev-sec/mysql-baseline/pull/14) ([chris-rock](https://github.com/chris-rock)) 232 | - root should be owner of mysql config [\#13](https://github.com/dev-sec/mysql-baseline/pull/13) ([chris-rock](https://github.com/chris-rock)) 233 | 234 | ## [1.0.0](https://github.com/dev-sec/mysql-baseline/tree/1.0.0) (2014-08-13) 235 | 236 | [Full Changelog](https://github.com/dev-sec/mysql-baseline/compare/498e61287ce653cd1dce5b867c9f112f5bc0776a...1.0.0) 237 | 238 | **Merged pull requests:** 239 | 240 | - added kitchen test for secure-auth optionen and updated requirement number [\#12](https://github.com/dev-sec/mysql-baseline/pull/12) ([atomic111](https://github.com/atomic111)) 241 | - Ensure serverspec does not fail with wrong cli languages [\#11](https://github.com/dev-sec/mysql-baseline/pull/11) ([chris-rock](https://github.com/chris-rock)) 242 | - test only if we have distinct hardening file. \(not the case in the puppet [\#10](https://github.com/dev-sec/mysql-baseline/pull/10) ([ehaselwanter](https://github.com/ehaselwanter)) 243 | - update tests for all supported platform [\#9](https://github.com/dev-sec/mysql-baseline/pull/9) ([ehaselwanter](https://github.com/ehaselwanter)) 244 | - add some GIS requirements [\#8](https://github.com/dev-sec/mysql-baseline/pull/8) ([ehaselwanter](https://github.com/ehaselwanter)) 245 | - update with common rubocop stuff and fixes [\#7](https://github.com/dev-sec/mysql-baseline/pull/7) ([ehaselwanter](https://github.com/ehaselwanter)) 246 | - add standalone usage to mysql test [\#6](https://github.com/dev-sec/mysql-baseline/pull/6) ([ehaselwanter](https://github.com/ehaselwanter)) 247 | - add lockfiles and delete them from tree [\#5](https://github.com/dev-sec/mysql-baseline/pull/5) ([ehaselwanter](https://github.com/ehaselwanter)) 248 | - streamline .rubocop config [\#4](https://github.com/dev-sec/mysql-baseline/pull/4) ([ehaselwanter](https://github.com/ehaselwanter)) 249 | - rubocop fixes [\#3](https://github.com/dev-sec/mysql-baseline/pull/3) ([ehaselwanter](https://github.com/ehaselwanter)) 250 | - add puppet configs for the default test suite [\#2](https://github.com/dev-sec/mysql-baseline/pull/2) ([ehaselwanter](https://github.com/ehaselwanter)) 251 | - add the tests from https://github.com/TelekomLabs/chef-mysql-hardening [\#1](https://github.com/dev-sec/mysql-baseline/pull/1) ([ehaselwanter](https://github.com/ehaselwanter)) 252 | 253 | 254 | 255 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 256 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'cookstyle' 6 | gem 'highline' 7 | gem 'rack' 8 | gem 'rake' 9 | gem 'rubocop' 10 | 11 | group :tools do 12 | gem 'github_changelog_generator' 13 | gem 'pry-coolline' 14 | end 15 | 16 | source 'https://packagecloud.io/cinc-project/stable' do 17 | gem 'chef-config' 18 | gem 'cinc-auditor-bin' 19 | end 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevSec MySQL Baseline 2 | 3 | This Compliance Profile ensures, that all hardening projects keep the same quality. 4 | 5 | - 6 | - 7 | - 8 | 9 | ## Standalone Usage 10 | 11 | This Compliance Profile requires [cinc-auditor](https://cinc.sh/start/auditor/) or [InSpec](https://github.com/chef/inspec) for execution: 12 | 13 | ```bash 14 | git clone https://github.com/dev-sec/mysql-baseline 15 | cinc-auditor exec mysql-baseline 16 | ``` 17 | 18 | You can also execute the profile directly from Github: 19 | 20 | ```bash 21 | cinc-auditor exec https://github.com/dev-sec/mysql-baseline 22 | ``` 23 | 24 | ## License and Author 25 | 26 | - Author:: Edmund Haselwanter 27 | - Author:: Dominik Richter 28 | - Author:: Patrick Muench 29 | - Author:: Christoph Hartmann 30 | 31 | - Copyright 2014, Deutsche Telekom AG 32 | - Copyright 2014-2016, The Hardening Framework Team 33 | - Copyright 2016-2022, DevSec Hardening Framework Team 34 | 35 | ```text 36 | Licensed under the Apache License, Version 2.0 (the "License"); 37 | you may not use this file except in compliance with the License. 38 | You may obtain a copy of the License at 39 | 40 | http://www.apache.org/licenses/LICENSE-2.0 41 | 42 | Unless required by applicable law or agreed to in writing, software 43 | distributed under the License is distributed on an "AS IS" BASIS, 44 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 45 | See the License for the specific language governing permissions and 46 | limitations under the License. 47 | ``` 48 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # frozen_string_literal: true 3 | 4 | require 'cookstyle' 5 | require 'rake/testtask' 6 | require 'rubocop/rake_task' 7 | 8 | # Rubocop 9 | desc 'Run Rubocop lint checks' 10 | task :rubocop do 11 | RuboCop::RakeTask.new 12 | end 13 | 14 | RuboCop::RakeTask.new(:cookstyle) do |task| 15 | task.options << '--display-cop-names' 16 | end 17 | 18 | # lint the project 19 | desc 'Run robocop linter' 20 | task lint: [:rubocop] 21 | 22 | # run tests 23 | task default: [:lint, 'test:check'] 24 | 25 | namespace :test do 26 | # run inspec check to verify that the profile is properly configured 27 | task :check do 28 | require 'inspec' 29 | puts "Checking profile with InSpec Version: #{Inspec::VERSION}" 30 | profile = Inspec::Profile.for_target('.', backend: Inspec::Backend.create(Inspec::Config.mock)) 31 | pp profile.check 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /controls/mysql_conf.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2014, Deutsche Telekom AG 4 | # Copyright:: 2018, Christoph Hartmann 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | mysql_hardening_file = '/etc/mysql/conf.d/hardening.cnf' 20 | 21 | user = input('user') 22 | pass = input('password') 23 | 24 | # get mysql version and distribution 25 | version = mysql_version(user, pass) 26 | distribution = mysql_distribution(user, pass) 27 | 28 | # get datadir and logfile-path from settings in the configuration if it is defined or from mysql itself 29 | mysql_data_path = if mysql_conf&.params&.mysqld&.datadir 30 | mysql_conf.params.mysqld.datadir 31 | else 32 | command("mysql -u#{user} -p#{pass} -sN -e \"select @@GLOBAL.datadir\";").stdout.strip 33 | end 34 | 35 | mysql_log_file = if mysql_conf.params.mysqld && mysql_conf.params.mysqld['log-error'] 36 | mysql_conf.params.mysqld['log-error'] 37 | else 38 | command("mysql -u#{user} -p#{pass} -sN -e \"select @@GLOBAL.log_error\";").stdout.strip 39 | end 40 | 41 | # set OS-dependent filenames and paths 42 | case os[:family] 43 | when 'ubuntu', 'debian' 44 | mysql_config_path = '/etc/mysql/' 45 | mysql_config_file = "#{mysql_config_path}my.cnf" 46 | mysql_log_group = 'adm' 47 | process_name = 'mysqld' 48 | process_name = 'mariadbd' if os[:release] >= '11' && os[:name] == 'debian' 49 | when 'redhat', 'fedora' 50 | mysql_config_path = '/etc/' 51 | mysql_config_file = "#{mysql_config_path}my.cnf" 52 | mysql_log_group = 'mysql' 53 | process_name = 'mysqld' 54 | process_name = 'mariadbd' if os[:release] >= '9' 55 | when 'suse' 56 | mysql_config_path = '/etc/' 57 | mysql_config_file = "#{mysql_config_path}my.cnf" 58 | mysql_log_group = 'mysql' 59 | process_name = 'mysqld' 60 | end 61 | 62 | # 'Check for multiple instances' do 63 | control 'mysql-conf-02' do 64 | impact 0.5 65 | title 'only one instance of mysql should run on a server' 66 | describe command("pgrep -x -c #{process_name}") do 67 | its(:stdout) { should match(/^1$/) } 68 | end 69 | end 70 | 71 | # Parsing configfiles for unwanted entries 72 | control 'mysql-conf-03' do 73 | impact 0.7 74 | title 'enable secure configurations for mysql' 75 | describe mysql_conf.params('mysqld') do 76 | its('safe-user-create') { should cmp 1 } 77 | its('old_passwords') { should_not cmp 1 } 78 | its('user') { should cmp 'mysql' } 79 | its('secure-file-priv') { should_not eq nil } 80 | its('local-infile') { should cmp 0 } 81 | its('skip-symbolic-links') { should cmp 1 } 82 | its('skip-show-database') { should eq '' } 83 | its('skip-grant-tables') { should eq nil } 84 | its('allow-suspicious-udfs') { should cmp 0 } 85 | end 86 | end 87 | 88 | # Parsing configfiles for unwanted entries 89 | control 'mysql-conf-03b' do 90 | impact 0.7 91 | title 'enable secure configurations for mysql' 92 | only_if('MySQL version less than 8.0.3 and not mariadb') do 93 | (Gem::Version.new(version.mysql_version) <= Gem::Version.new('8.0.3') and distribution.mysql_distribution == 'mysql') 94 | end 95 | describe mysql_conf.params('mysqld') do 96 | its('secure-auth') { should cmp 1 } 97 | end 98 | end 99 | 100 | # 'Mysql-config: owner, group and permissions' 101 | control 'mysql-conf-04' do 102 | impact 0.7 103 | title 'ensure the mysql data path is owned by mysql user' 104 | describe file(mysql_data_path) do 105 | it { should be_directory } 106 | it { should be_owned_by 'mysql' } 107 | it { should be_grouped_into 'mysql' } 108 | end 109 | end 110 | 111 | control 'mysql-conf-05' do 112 | impact 0.5 113 | title 'ensure data path is owned by mysql user' 114 | describe file("#{mysql_data_path}/ibdata1") do 115 | it { should be_owned_by 'mysql' } 116 | it { should be_grouped_into 'mysql' } 117 | it { should_not be_readable.by('others') } 118 | it { should_not be_writable.by('others') } 119 | it { should_not be_executable.by('others') } 120 | end 121 | end 122 | 123 | control 'mysql-conf-06' do 124 | impact 0.5 125 | title 'ensure log file is owned by mysql user' 126 | only_if { mysql_log_file != '' } 127 | describe file(mysql_log_file) do 128 | it { should be_owned_by 'mysql' } 129 | it { should be_grouped_into mysql_log_group } 130 | it { should_not be_readable.by('others') } 131 | it { should_not be_writable.by('others') } 132 | it { should_not be_executable.by('others') } 133 | end 134 | end 135 | 136 | control 'mysql-conf-07' do 137 | impact 0.7 138 | title 'ensure the mysql config file is owned by user root, group mysql' 139 | describe file(mysql_config_file) do 140 | it { should be_file } 141 | it { should be_owned_by 'root' } 142 | it { should be_grouped_into 'mysql' } 143 | it { should_not be_readable.by('others') } 144 | end 145 | end 146 | 147 | # test this only if we have a mysql_hardening_file 148 | control 'mysql-conf-08' do 149 | impact 0.5 150 | title 'ensure the mysql hardening config file is owned by user root, group mysql' 151 | describe file(mysql_hardening_file) do 152 | it { should be_owned_by 'root' } 153 | it { should be_grouped_into 'mysql' } 154 | it { should_not be_readable.by('others') } 155 | end 156 | only_if { command("ls #{mysql_hardening_file}").exit_status.zero? } 157 | end 158 | 159 | # 'Mysql environment' 160 | control 'mysql-conf-09' do 161 | impact 1.0 162 | title 'the use of MYSQL_PWD is insecure and can exploit the password' 163 | describe command('env') do 164 | its(:stdout) { should_not match(/^MYSQL_PWD=/) } 165 | end 166 | end 167 | -------------------------------------------------------------------------------- /controls/mysql_db.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2014, Deutsche Telekom AG 4 | # Copyright:: 2018, Christoph Hartmann 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | user = input('user') 20 | pass = input('password') 21 | 22 | control 'mysql-db-01' do 23 | impact 0.3 24 | title 'use supported mysql version in production' 25 | describe command("mysql -u#{user} -p#{pass} -sN -e 'select version();'") do 26 | its(:stdout) { should_not match(/Community/) } 27 | end 28 | end 29 | 30 | control 'mysql-db-02' do 31 | impact 0.5 32 | title 'use mysql version 5 or higher' 33 | describe command("mysql -u#{user} -p#{pass} -sN -e 'select substring_index(version(),\".\",1);'") do 34 | its(:stdout) { should cmp >= 5 } 35 | end 36 | end 37 | 38 | control 'mysql-db-03' do 39 | impact 1.0 40 | title 'test database must be deleted' 41 | describe command("mysql -u#{user} -p#{pass} -sN -e 'show databases like \"test\";'") do 42 | its(:stdout) { should_not match(/test/) } 43 | end 44 | end 45 | 46 | control 'mysql-db-04' do 47 | impact 1.0 48 | title 'deactivate anonymous user names' 49 | describe command("mysql -u#{user} -p#{pass} -sN -e 'select count(*) from mysql.user where user=\"\";'") do 50 | its(:stdout) { should match(/^0/) } 51 | end 52 | end 53 | 54 | # current versions of MySQL support roles, we don't check them at the moment. 55 | 56 | if command("mysql -u#{user} -p#{pass} -sN -e 'SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = \"mysql\" AND TABLE_NAME = \"user\" AND COLUMN_NAME = \"is_role\"'").stdout.strip == '0' 57 | role_filter = '' 58 | else 59 | role_filter = 'AND CONVERT(is_role USING utf8) = "N"' 60 | end 61 | 62 | # MySQL 5.7.6 dropped the "password" column in the mysql.user table 63 | # so we have to check if it's there before we check if a password is empty 64 | control 'mysql-db-05' do 65 | impact 1.0 66 | title 'default passwords must be changed' 67 | only_if { command("mysql -u#{user} -p#{pass} -sN -e 'select count(*) from information_schema.columns where table_name=\"user\" and table_schema=\"mysql\" and column_name=\"password\";'").stdout.strip == '1' } 68 | describe command("mysql -u#{user} -p#{pass} -sN -e 'select count(*) from mysql.user where (length(password)=0 or password=\"\") and (length(authentication_string)=0 or authentication_string=\"\") and not (user=\"mariadb.sys\" and host=\"localhost\") #{role_filter};'") do 69 | its(:stdout) { should match(/^0/) } 70 | end 71 | end 72 | 73 | # MySQL versions older than 5.7.6 and MariaDB databases still have the 74 | # password column so we need to check if it is empty 75 | control 'mysql-db-05b' do 76 | impact 1.0 77 | title 'default passwords must be changed' 78 | only_if { command("mysql -u#{user} -p#{pass} -sN -e 'select count(*) from information_schema.columns where table_name=\"user\" and table_schema=\"mysql\" and column_name=\"password\";'").stdout.strip == '0' } 79 | describe command("mysql -u#{user} -p#{pass} -sN -e 'select count(*) from mysql.user where length(authentication_string)=0 or authentication_string=\"\" and not (user=\"mariadb.sys\" and host=\"localhost\") #{role_filter};'") do 80 | its(:stdout) { should match(/^0/) } 81 | end 82 | end 83 | 84 | control 'mysql-db-06' do 85 | impact 0.5 86 | title 'the grant option must not be used' 87 | describe command("mysql -u#{user} -p#{pass} -sN -e 'select count(*) from mysql.user where grant_priv=\"y\" and user!=\"root\" and user!=\"debian-sys-maint\" and not (user=\"mysql\" and host=\"localhost\");'") do 88 | its(:stdout) { should match(/^0/) } 89 | end 90 | end 91 | 92 | control 'mysql-db-07' do 93 | impact 0.5 94 | title 'ensure no wildcards are used for hostnames' 95 | describe command("mysql -u#{user} -p#{pass} -sN -e 'select count(*) from mysql.user where host=\"%\"'") do 96 | its(:stdout) { should match(/^0/) } 97 | end 98 | end 99 | 100 | control 'mysql-db-08' do 101 | impact 0.5 102 | title 'it must be ensured that superuser can login via localhost only' 103 | describe command("mysql -u#{user} -p#{pass} -sN -e 'select count(*) from mysql.user where user=\"root\" and host not in (\"localhost\",\"127.0.0.1\",\"::1\")'") do 104 | its(:stdout) { should match(/^0/) } 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /inspec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: mysql-baseline 3 | title: DevSec MySQL Baseline 4 | maintainer: DevSec Hardening Framework Team 5 | copyright: DevSec Hardening Framework Team 6 | copyright_email: hello@dev-sec.io 7 | license: Apache-2.0 8 | summary: Test-suite for best-practice mysql hardening 9 | inspec_version: '>= 4.6.3' 10 | version: 5.1.0 11 | supports: 12 | - os-family: unix 13 | inputs: 14 | - name: 'user' 15 | description: 'MySQL database user' 16 | value: 'root' 17 | required: true 18 | - name: 'password' 19 | description: 'MySQL database password' 20 | value: 'iloverandompasswordsbutthiswilldo' 21 | required: true 22 | sensitive: true 23 | -------------------------------------------------------------------------------- /libraries/mysql_distribution.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2020, Sebastian Gumprich 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # author: Sebastian Gumprich 18 | 19 | class MySQLDistribution < Inspec.resource(1) 20 | name 'mysql_distribution' 21 | 22 | attr_reader :user, :pass 23 | 24 | def initialize(user, pass) 25 | super() 26 | @user = user 27 | @pass = pass 28 | end 29 | 30 | def mysql_distribution 31 | if inspec.command("mysql -u#{user} -p#{pass} -sN -e 'SHOW VARIABLES WHERE Variable_name = \"version_comment\"'").stdout.strip.split("\t")[1].downcase.include? 'mariadb' 32 | 'mariadb' 33 | else 34 | 'mysql' 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /libraries/mysql_version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2020, Sebastian Gumprich 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # author: Sebastian Gumprich 18 | 19 | class MySQLVersion < Inspec.resource(1) 20 | name 'mysql_version' 21 | 22 | attr_reader :user, :pass 23 | 24 | def initialize(user, pass) 25 | super() 26 | @user = user 27 | @pass = pass 28 | end 29 | 30 | def mysql_version 31 | inspec.command("mysql -sN -e 'SHOW VARIABLES WHERE variable_name = \"version\"'").stdout.strip.split("\t")[1].split('-')[0].to_s 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", 5 | ":gitSignOff" 6 | ], 7 | "dependencyDashboard": true, 8 | "dependencyDashboardAutoclose": true, 9 | "packageRules": [ 10 | { 11 | "matchUpdateTypes": ["patch", "minor"], 12 | "automerge": true 13 | } 14 | ] 15 | } 16 | --------------------------------------------------------------------------------