├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .goreleaser.yml ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── app.go ├── app_test.go ├── cfg ├── cis-1.0 │ ├── config.yaml │ └── definitions.yaml ├── cis-1.1 │ ├── config.yaml │ └── definitions.yaml ├── cis-1.2 │ ├── config.yaml │ └── definitions.yaml ├── cis-1.6.0 │ ├── config.yaml │ └── definitions.yaml └── ocp-3.9 │ ├── config.yaml │ └── definitions.yaml ├── go.mod ├── go.sum ├── main.go ├── makefile ├── root.go └── utils.go /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths-ignore: 7 | - "*.md" 8 | - "LICENSE" 9 | - "NOTICE" 10 | pull_request: 11 | paths-ignore: 12 | - "*.md" 13 | - "LICENSE" 14 | - "NOTICE" 15 | jobs: 16 | build: 17 | name: Build 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - name: Setup Go 21 | uses: actions/setup-go@v5 22 | with: 23 | go-version: 1.21.6 24 | - name: Checkout code 25 | uses: actions/checkout@v4 26 | - name: Run unit tests 27 | run: make tests 28 | - name: Upload code coverage 29 | uses: codecov/codecov-action@v3 30 | with: 31 | file: ./coverage.txt 32 | - name: Dry-run release snapshot 33 | uses: goreleaser/goreleaser-action@v5 34 | with: 35 | version: v1.7.0 36 | args: release --snapshot --skip-publish --rm-dist 37 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - "v*" 6 | jobs: 7 | release: 8 | name: Release 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - name: Setup Go 12 | uses: actions/setup-go@v5 13 | with: 14 | go-version: 1.21.6 15 | - name: Checkout code 16 | uses: actions/checkout@v4 17 | - name: Run unit tests 18 | run: make tests 19 | - name: Upload code coverage 20 | uses: codecov/codecov-action@v3 21 | with: 22 | file: ./coverage.txt 23 | - name: Release 24 | uses: goreleaser/goreleaser-action@v5 25 | with: 26 | version: v1.7.0 27 | args: release --rm-dist 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | docker-bench 3 | coverage.txt 4 | dist 5 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | env: 2 | - GO111MODULE=on 3 | - LINUXBENCH_CFG=/etc/docker-bench/cfg 4 | builds: 5 | - main: . 6 | binary: docker-bench 7 | goos: 8 | - linux 9 | goarch: 10 | - amd64 11 | ldflags: 12 | - "-X github.com/aquasecurity/docker-bench/root.cfgDir={{.Env.LINUXBENCH_CFG}}" 13 | # Archive customization 14 | archives: 15 | - id: compress 16 | format: tar.gz 17 | files: 18 | - "cfg/**/*" 19 | nfpms: 20 | - vendor: Aqua Security 21 | description: "Checks whether Docker is deployed according to security best practices as defined in the CIS Docker Benchmark" 22 | license: Apache-2.0 23 | homepage: https://github.com/aquasecurity/docker-bench 24 | contents: 25 | - src: "cfg/**/*" 26 | dst: "/etc/docker-bench/cfg" 27 | formats: 28 | - deb 29 | - rpm -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thank you for taking interest in contributing to docker-bench ! 2 | ## Issues 3 | 4 | - Feel free to open issues for any reason as long as you make it clear if this issue is about a bug/feature/question/comment. 5 | - Please spend a small amount of time giving due diligence to the issue tracker. Your issue might be a duplicate. If it is, please add your comment to the existing issue. 6 | - Remember users might be searching for your issue in the future, so please give it a meaningful title to help others. 7 | - The issue should clearly explain the reason for opening, the proposal if you have any, and any relevant technical information. 8 | 9 | ## Pull Requests 10 | 11 | 1. Every Pull Request should have an associated Issue unless you are fixing a trivial documentation issue. 12 | 1. Your PR is more likely to be accepted if it focuses on just one change. 13 | 1. Describe what the PR does. There's no convention enforced, but please try to be concise and descriptive. Treat the PR description as a commit message. Titles that starts with "fix"/"add"/"improve"/"remove" are good examples. 14 | 1. Please add the associated Issue in the PR description. 15 | 1. There's no need to add or tag reviewers. 16 | 1. If a reviewer commented on your code or asked for changes, please remember to mark the discussion as resolved after you address it. PRs with unresolved issues should not be merged (even if the comment is unclear or requires no action from your side). 17 | 1. Please include a comment with the results before and after your change. 18 | 1. Your PR is more likely to be accepted if it includes tests (We have not historically been very strict about tests, but we would like to improve this!). 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | docker-bench 2 | Copyright 2017-2019 Aqua Security Software Ltd. 3 | 4 | This product includes software developed by Aqua Security (https://aquasec.com). 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub Release][release-img]][release] 2 | [![License][license-img]][license] 3 | [![Coverage Status][cov-img]][cov] 4 | [![GitHub Build Actions][build-action-img]][actions] 5 | [![GitHub Release Actions][release-action-img]][actions] 6 | 7 | Docker-bench is a Go application that checks whether Docker is deployed securely by running the checks documented in the [CIS Docker Benchmark](https://www.cisecurity.org/benchmark/docker/). 8 | 9 | Tests are configured with YAML files, making this tool easy to update as test specifications evolve. 10 | 11 | 12 | ## CIS Docker Benchmark support 13 | 14 | docker-bench currently supports tests as defined in the following CIS Docker Benchmarks: 15 | 16 | | CIS Benchmark | docker-bench cfg directory | Docker versions | 17 | |--------------------------------------------------------------------------------------------------|------------------------------|----------------------------------| 18 | | [CIS Docker Benchmark v1.6.0](https://workbench.cisecurity.org/benchmarks/11818) | [cis-1.6.0](./cfg/cis-1.6.0) | 20.10 | 19 | | [CIS Docker Benchmark v1.2.0](https://workbench.cisecurity.org/benchmarks/601) | [cis-1.2](./cfg/cis-1.2) | 18.09 and Docker Enterprise 2.1 | 20 | | [CIS Docker Community Edition Benchmark v1.1.0](https://workbench.cisecurity.org/benchmarks/552) | [cis-1.1](./cfg/cis-1.1) | 17.06 | 21 | | [CIS Docker 1.13.0 Benchmark v1.0.0](https://workbench.cisecurity.org/benchmarks/363) | [cis-1.0](./cfg/cis-1.0) | 1.13.0 | 22 | 23 | 24 | docker-bench will determine the test set to run based on the Docker version running on the host machine. 25 | The version to run tests for can also be specified manually with the `--version ` or `--benchmark ` commandline flag. 26 | 27 | ## Installation 28 | ### Installing from sources 29 | 30 | Install [Go](https://golang.org/doc/install), then 31 | clone this repository and run as follows (assuming your [$GOPATH is set](https://github.com/golang/go/wiki/GOPATH)): 32 | 33 | ```shell 34 | go get github.com/aquasecurity/docker-bench 35 | cd $GOPATH/src/github.com/aquasecurity/docker-bench 36 | go build -o docker-bench . 37 | 38 | # See all supported options 39 | ./docker-bench --help 40 | 41 | # Run checks 42 | ./docker-bench 43 | 44 | # Run checks for specified Docker version 45 | ./docker-bench --version 18.09 46 | 47 | # Run checks for specified cis Benchmark 48 | ./docker-bench --benchmark cis-1.2 49 | ``` 50 | 51 | # Tests 52 | Tests are specified in definition files `cfg//definitions.yaml`, 53 | where `` is the version of CIS for which the test applies. 54 | 55 | # Contributing 56 | We welcome PRs and issue reports. 57 | Your PR is more likely to be accepted if it focuses on just one change. 58 | Please include a comment with the results before and after your change. 59 | Your PR is more likely to be accepted if it includes tests. (We have not historically been very strict about tests, but we would like to improve this!). 60 | You're welcome to submit a draft PR if you would like early feedback on an idea or an approach. 61 | Happy coding! 62 | 63 | [actions]: https://github.com/aquasecurity/docker-bench/actions 64 | [build-action-img]: https://github.com/aquasecurity/docker-bench/workflows/build/badge.svg 65 | [cov-img]: https://codecov.io/github/aquasecurity/docker-bench/branch/main/graph/badge.svg 66 | [cov]: https://codecov.io/github/aquasecurity/docker-bench 67 | [license-img]: https://img.shields.io/badge/License-Apache%202.0-blue.svg 68 | [license]: https://opensource.org/licenses/Apache-2.0 69 | [release-img]: https://img.shields.io/github/release/aquasecurity/docker-bench.svg 70 | [release]: https://github.com/aquasecurity/docker-bench/releases 71 | [release-action-img]: https://github.com/aquasecurity/docker-bench/workflows/release/badge.svg 72 | -------------------------------------------------------------------------------- /app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/aquasecurity/bench-common/check" 11 | "github.com/aquasecurity/bench-common/util" 12 | "github.com/golang/glog" 13 | "github.com/hashicorp/go-version" 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | var benchmarkVersionMap = map[string]string{ 18 | "cis-1.6.0": ">= 20.10", 19 | "cis-1.2": ">= 18.09, < 20.10", 20 | "cis-1.1": ">= 17.06, < 18.09", 21 | "cis-1.0": ">= 1.13.0, < 17.06", 22 | } 23 | 24 | func app(cmd *cobra.Command, args []string) { 25 | 26 | version, err := ResolveCisVersion(benchmarkVersion, dockerVersion) 27 | if err != nil { 28 | util.ExitWithError(err) 29 | } 30 | 31 | path, err := getFilePath(version, "definitions.yaml") 32 | if err != nil { 33 | util.ExitWithError(err) 34 | } 35 | 36 | constraints, err := getConstraints() 37 | if err != nil { 38 | util.ExitWithError(err) 39 | } 40 | 41 | configPath, _ := getFilePath(version, "config.yaml") 42 | // Not checking for error because if file doesn't exist then it just nil and ignore. 43 | 44 | controls, err := getControls(path, configPath, constraints) 45 | if err != nil { 46 | util.ExitWithError(err) 47 | } 48 | 49 | summary := runControls(controls, checkList) 50 | err = outputResults(controls, summary) 51 | if err != nil { 52 | util.ExitWithError(err) 53 | } 54 | } 55 | 56 | // ResolveCisVersion returns final cis version to use. 57 | func ResolveCisVersion(benchmarkVersion, dockerVersion string) (string, error) { 58 | var version string 59 | var err error 60 | 61 | // Benchmark flag is specify 62 | if benchmarkVersion != "" { 63 | 64 | // Check for not specify both --version and --benchmark 65 | if dockerVersion != "" { 66 | return "", fmt.Errorf("It is an error to specify both --version and --benchmark flags") 67 | } 68 | 69 | // Set given CIS benchmark version eg cis-1.2 70 | version = benchmarkVersion 71 | } else { 72 | // Auto-detect the version of Docker that's running 73 | if dockerVersion == "" { 74 | dockerVersion, err = getDockerVersion() 75 | if err != nil { 76 | return "", fmt.Errorf("Version check failed: %s\nAlternatively, you can specify the version with --version", err) 77 | } 78 | } 79 | // Set appropriate CIS benchmark version according to docker version 80 | version, err = getDockerCisVersion(dockerVersion) 81 | if err != nil { 82 | return "", fmt.Errorf("Failed to get a valid CIS benchmark version for Docker version %s: %v", dockerVersion, err) 83 | } 84 | } 85 | return version, nil 86 | } 87 | 88 | func outputResults(controls *check.Controls, summary check.Summary) error { 89 | // if we successfully ran some tests and it's json format, ignore the warnings 90 | if (summary.Fail > 0 || summary.Warn > 0 || summary.Pass > 0) && jsonFmt { 91 | out, err := controls.JSON() 92 | if err != nil { 93 | // util.ExitWithError(fmt.Errorf("failed to output in JSON format: %v", err)) 94 | return err 95 | } 96 | util.PrintOutput(string(out), outputFile) 97 | } else { 98 | util.PrettyPrint(controls, summary, noRemediations, includeTestOutput) 99 | } 100 | 101 | return nil 102 | } 103 | 104 | func runControls(controls *check.Controls, checkList string) check.Summary { 105 | var summary check.Summary 106 | 107 | if checkList != "" { 108 | ids := util.CleanIDs(checkList) 109 | summary = controls.RunChecks(ids...) 110 | } else { 111 | summary = controls.RunGroup() 112 | } 113 | 114 | return summary 115 | } 116 | 117 | func getControls(path, substitutionFile string, constraints []string) (*check.Controls, error) { 118 | data, err := os.ReadFile(path) 119 | if err != nil { 120 | return nil, err 121 | } 122 | s := string(data) 123 | if substitutionFile != "" { 124 | substitutionData, err := os.ReadFile(substitutionFile) 125 | if err != nil { 126 | return nil, err 127 | } 128 | substituMap, err := util.GetSubstitutionMap(substitutionData) 129 | if err != nil { 130 | return nil, err 131 | } 132 | s = util.MakeSubstitutions(s, "", substituMap) 133 | } 134 | controls, err := check.NewControls([]byte(s), constraints) 135 | if err != nil { 136 | return nil, err 137 | } 138 | 139 | return controls, err 140 | } 141 | 142 | // getDockerVersion returns the docker server engine version. 143 | func getDockerVersion() (string, error) { 144 | cmd := exec.Command("docker", "version", "-f", "{{.Server.Version}}") 145 | out, err := cmd.Output() 146 | if err != nil { 147 | return "", err 148 | } 149 | return strings.TrimSpace(string(out)), nil 150 | } 151 | 152 | func getFilePath(version string, filename string) (string, error) { 153 | 154 | glog.V(2).Info(fmt.Sprintf("Looking for config for %s", version)) 155 | 156 | path := filepath.Join(cfgDir, version) 157 | file := filepath.Join(path, filename) 158 | 159 | glog.V(2).Info(fmt.Sprintf("Looking for config file: %s\n", file)) 160 | 161 | _, err := os.Stat(file) 162 | if err != nil { 163 | return "", err 164 | } 165 | 166 | return file, nil 167 | } 168 | 169 | func getConstraints() (constraints []string, err error) { 170 | swarmStatus, err := GetDockerSwarm() 171 | if err != nil { 172 | glog.V(1).Info(fmt.Sprintf("Failed to get docker swarm status, %s", err)) 173 | } 174 | 175 | constraints = append(constraints, 176 | fmt.Sprintf("docker-swarm=%s", swarmStatus), 177 | ) 178 | 179 | glog.V(1).Info(fmt.Sprintf("The constraints are:, %s", constraints)) 180 | return constraints, nil 181 | } 182 | 183 | // getDockerCisVersion select the correct CIS version in compare to running docker version 184 | // TBD ocp-3.9 auto detection 185 | func getDockerCisVersion(stringVersion string) (string, error) { 186 | dockerVersion, err := trimVersion(stringVersion) 187 | 188 | if err != nil { 189 | return "", err 190 | } 191 | 192 | for benchVersion, dockerConstraints := range benchmarkVersionMap { 193 | currConstraints, err := version.NewConstraint(dockerConstraints) 194 | if err != nil { 195 | return "", err 196 | } 197 | if currConstraints.Check(dockerVersion) { 198 | glog.V(2).Info(fmt.Sprintf("docker version %s satisfies constraints %s", dockerVersion, currConstraints)) 199 | return benchVersion, nil 200 | } 201 | } 202 | 203 | tooOldVersion, err := version.NewConstraint("< 1.13.0") 204 | if err != nil { 205 | return "", err 206 | } 207 | 208 | // Vesions before 1.13.0 are not supported by CIS. 209 | if tooOldVersion.Check(dockerVersion) { 210 | return "", fmt.Errorf("docker version %s is too old", stringVersion) 211 | } 212 | 213 | return "", fmt.Errorf("no suitable CIS version has been found for docker version %s", stringVersion) 214 | } 215 | 216 | // TrimVersion function remove all Matadate or Prerelease parts 217 | // because constraints.Check() can't handle comparison with it. 218 | func trimVersion(stringVersion string) (*version.Version, error) { 219 | currVersion, err := version.NewVersion(stringVersion) 220 | if err != nil { 221 | return nil, err 222 | } 223 | 224 | if currVersion.Metadata() != "" || currVersion.Prerelease() != "" { 225 | tempStrVersion := strings.Trim(strings.Replace(fmt.Sprint(currVersion.Segments()), " ", ".", -1), "[]") 226 | currVersion, err = version.NewVersion(tempStrVersion) 227 | if err != nil { 228 | return nil, err 229 | } 230 | } 231 | 232 | return currVersion, nil 233 | } 234 | -------------------------------------------------------------------------------- /app_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | cfgdir = "./cfg" 12 | ver = "cis-1.2" 13 | ) 14 | 15 | func TestGetDockerVersion(t *testing.T) { 16 | _, err := getDockerVersion() 17 | if err != nil { 18 | t.Errorf("unexpected error: %s", err) 19 | } 20 | } 21 | 22 | // Tests all standard docker-bench defintion files 23 | func TestGetFilePath(t *testing.T) { 24 | d, err := os.Open(cfgdir) 25 | if err != nil { 26 | t.Errorf("unexpected error: %s\n", err) 27 | } 28 | 29 | vers, err := d.Readdirnames(-1) 30 | if err != nil { 31 | t.Errorf("unexpected error: %s\n", err) 32 | } 33 | 34 | for _, ver := range vers { 35 | _, err := getFilePath(ver, "definitions.yaml") 36 | if err != nil { 37 | t.Errorf("unexpected error: %s\n", err) 38 | } 39 | } 40 | _, err = getFilePath("Gibrish", "definitions.yaml") 41 | if err.Error() != "stat cfg/Gibrish/definitions.yaml: no such file or directory" { 42 | t.Errorf("unexpected error: %s\n", err) 43 | } 44 | _, err = getFilePath("cis-1.2", "Gibrish.yaml") 45 | if err.Error() != "stat cfg/cis-1.2/Gibrish.yaml: no such file or directory" { 46 | t.Errorf("unexpected error: %s\n", err) 47 | } 48 | } 49 | 50 | func TestGetControls(t *testing.T) { 51 | tests := []struct { 52 | name string 53 | benchmarkVersion string 54 | cfgFile string 55 | substitutionFile string 56 | constraints []string 57 | wantErr bool 58 | }{ 59 | {name: "Test for valid control", benchmarkVersion: "cis-1.2", cfgFile: "definitions.yaml", substitutionFile: "config.yaml", constraints: []string{"docker-swarm=inactive"}, wantErr: false}, 60 | {name: "Test for valid control no constraints", benchmarkVersion: "cis-1.2", cfgFile: "definitions.yaml", substitutionFile: "config.yaml", wantErr: false}, 61 | {name: "Test for invalid benchmarkVersion", benchmarkVersion: "sadf", cfgFile: "definitions.yaml", substitutionFile: "config.yaml", wantErr: true}, 62 | {name: "Test for invalid cfg file", benchmarkVersion: "cis-1.2", cfgFile: "asd", substitutionFile: "config.yaml", wantErr: true}, 63 | {name: "Test for invalid substitution file", benchmarkVersion: "cis-1.2", cfgFile: "definitions.yaml", substitutionFile: "asdf", constraints: []string{"docker-swarm=inactive"}, wantErr: true}, 64 | {name: "Test for empty", wantErr: true}, 65 | } 66 | for _, tt := range tests { 67 | t.Run(tt.name, func(t *testing.T) { 68 | version, _ := ResolveCisVersion(tt.benchmarkVersion, "") 69 | path, _ := getFilePath(version, tt.cfgFile) 70 | substitutionFile, _ := getFilePath(version, tt.substitutionFile) 71 | got, err := getControls(path, substitutionFile, tt.constraints) 72 | if err != nil && !tt.wantErr { 73 | t.Errorf("unexpected error = %v\nwantErr %v\nGot: %v", err, tt.wantErr, got) 74 | } 75 | }) 76 | } 77 | 78 | } 79 | 80 | func TestRunControls(t *testing.T) { 81 | var err error 82 | 83 | path, err := getFilePath(ver, "definitions.yaml") 84 | if err != nil { 85 | t.Errorf("unexpected error: %s\n", err) 86 | } 87 | configPath, err := getFilePath(ver, "config.yaml") 88 | if err != nil { 89 | t.Errorf("unexpected error: %s\n", err) 90 | } 91 | control, err := getControls(path, configPath, nil) 92 | if err != nil { 93 | t.Errorf("unexpected error: %s\n", err) 94 | } 95 | 96 | // Run all checks 97 | _ = runControls(control, "") 98 | 99 | // Run only specified checks 100 | checkList := "1.2, 2.1" 101 | _ = runControls(control, checkList) 102 | } 103 | 104 | func Test_getDockerCisVersion(t *testing.T) { 105 | tests := []struct { 106 | name string 107 | stringVersion string 108 | want string 109 | wantErr bool 110 | }{ 111 | {name: "Test for version 20.10", stringVersion: "20.10", want: "cis-1.6.0", wantErr: false}, 112 | {name: "Test for version 20.04", stringVersion: "20.04", want: "cis-1.2", wantErr: false}, 113 | {name: "Test for version 18.09", stringVersion: "18.09", want: "cis-1.2", wantErr: false}, 114 | {name: "Test for version 19.3.6", stringVersion: "19.3.6", want: "cis-1.2", wantErr: false}, 115 | {name: "Test for version 18.06", stringVersion: "18.06", want: "cis-1.1", wantErr: false}, 116 | {name: "Test for version 18.06.0-ce", stringVersion: "18.06.0-ce", want: "cis-1.1", wantErr: false}, 117 | {name: "Test for version 17.12", stringVersion: "17.12", want: "cis-1.1", wantErr: false}, 118 | {name: "Test for version 17.12-beta", stringVersion: "17.12-beta", want: "cis-1.1", wantErr: false}, 119 | {name: "Test for version 17.06", stringVersion: "17.06", want: "cis-1.1", wantErr: false}, 120 | {name: "Test for version 17.04", stringVersion: "17.04", want: "cis-1.0", wantErr: false}, 121 | {name: "Test for version 17.03", stringVersion: "17.03", want: "cis-1.0", wantErr: false}, 122 | {name: "Test for version 14.04", stringVersion: "14.04", want: "cis-1.0", wantErr: false}, 123 | {name: "Test for version 1.13.0", stringVersion: "1.13.0", want: "cis-1.0", wantErr: false}, 124 | {name: "Test for version 1.0.1", stringVersion: "1.0.1", want: "", wantErr: true}, 125 | {name: "Test for version asd", stringVersion: "asd", want: "", wantErr: true}, 126 | } 127 | for _, tt := range tests { 128 | t.Run(tt.name, func(t *testing.T) { 129 | got, err := getDockerCisVersion(tt.stringVersion) 130 | if (err != nil) != tt.wantErr { 131 | t.Errorf("getDockerCisVersion() error = %v, wantErr %v", err, tt.wantErr) 132 | return 133 | } 134 | if got != tt.want { 135 | t.Errorf("getDockerCisVersion() = %v, want %v", got, tt.want) 136 | } 137 | }) 138 | } 139 | } 140 | 141 | func Test_trimVersion(t *testing.T) { 142 | 143 | tests := []struct { 144 | name string 145 | stringVersion string 146 | want string 147 | }{ 148 | {name: "Test remove beta", stringVersion: "18.9.3-beta", want: "18.9.3"}, 149 | {name: "Test remove ce", stringVersion: "17.6-ce", want: "17.6.0"}, 150 | {name: "Test remove metadata", stringVersion: "16.3+rc231", want: "16.3.0"}, 151 | } 152 | for _, tt := range tests { 153 | t.Run(tt.name, func(t *testing.T) { 154 | got, err := trimVersion(tt.stringVersion) 155 | if err != nil { 156 | t.Errorf("trimVersion() error = %v", err) 157 | return 158 | } 159 | if !reflect.DeepEqual(got.String(), tt.want) { 160 | t.Errorf("trimVersion() = %v, want %v", got.String(), tt.want) 161 | } 162 | }) 163 | } 164 | } 165 | 166 | func Test_ResolveCisVersion(t *testing.T) { 167 | 168 | tests := []struct { 169 | name string 170 | dockerVersion string 171 | benchmarkVersion string 172 | wantErr bool 173 | expect string 174 | }{ 175 | {name: "Test docker version 18.9.3", dockerVersion: "18.9.3-beta", benchmarkVersion: "", expect: "cis-1.2"}, 176 | {name: "Test docker version 17.06.3", dockerVersion: "17.06.3", benchmarkVersion: "", expect: "cis-1.1"}, 177 | {name: "Test docker version 15", dockerVersion: "15", benchmarkVersion: "", expect: "cis-1.0"}, 178 | {name: "Test old docker version 1", dockerVersion: "1.11.1", benchmarkVersion: "", wantErr: true}, 179 | {name: "Test benchmark version cis-1.2", dockerVersion: "", benchmarkVersion: "cis-1.2", expect: "cis-1.2"}, 180 | {name: "Test benchmark version cis-1.1", dockerVersion: "", benchmarkVersion: "cis-1.1", expect: "cis-1.1"}, 181 | {name: "Test benchmark version cis-1.0", dockerVersion: "", benchmarkVersion: "cis-1.0", expect: "cis-1.0"}, 182 | {name: "Test both benchmark and docker version", dockerVersion: "16.3", benchmarkVersion: "cis-1.2", wantErr: true}, 183 | // {name: "Test empty", dockerVersion: "", benchmarkVersion: "", expect: "cis-1.2"}, # TBD after set env docker version. 184 | {name: "Test Non exist docker version", dockerVersion: "ghdsji", benchmarkVersion: "", wantErr: true}, 185 | } 186 | for _, tt := range tests { 187 | t.Run(tt.name, func(t *testing.T) { 188 | got, err := ResolveCisVersion(tt.benchmarkVersion, tt.dockerVersion) 189 | if err != nil && !tt.wantErr { 190 | t.Errorf("ResolveCisVersion() error = %v", err) 191 | return 192 | } 193 | if !reflect.DeepEqual(got, tt.expect) { 194 | t.Errorf("ResolveCisVersion() = %v, want %v", got, tt.expect) 195 | } 196 | }) 197 | } 198 | } 199 | 200 | func Test_getConstraints(t *testing.T) { 201 | got, err := getConstraints() 202 | if err != nil { 203 | t.Errorf("unexpected error: %s\n", err) 204 | } 205 | 206 | // Currently only one constraint exist - docker-swarm 207 | if !strings.Contains(got[0], "docker-swarm") { 208 | t.Errorf("expected output to contain docker-swarm\nGot:%v\n", got) 209 | } 210 | 211 | } 212 | -------------------------------------------------------------------------------- /cfg/cis-1.0/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ## Controls Files. 3 | # These are YAML files that hold all the details for running checks. 4 | # In here you can set all parameter substitution for docker-bench 5 | 6 | docker-storage: 7 | value: /var/lib/docker 8 | audit-log: 9 | value: /var/log/audit/audit.log 10 | -------------------------------------------------------------------------------- /cfg/cis-1.1/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ## Controls Files. 3 | # These are YAML files that hold all the details for running checks. 4 | # In here you can set all parameter substitution for docker-bench 5 | 6 | docker-storage: 7 | value: /var/lib/docker 8 | audit-log: 9 | value: /var/log/audit/audit.log 10 | -------------------------------------------------------------------------------- /cfg/cis-1.1/definitions.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | controls: 3 | id: "17.06" 4 | description: "CIS Docker Community Edition Benchmark" 5 | groups: 6 | - id: 1 7 | description: "Host Configuration" 8 | checks: 9 | - id: 1.1 10 | description: "Ensure a separate partition for containers has been created (Scored)" 11 | audit: grep $docker-storage /etc/fstab 12 | tests: 13 | test_items: 14 | - flag: "$docker-storage" 15 | set: true 16 | remediation: "For new installations, create a separate partition for $docker-storage 17 | mount point. For systems that were previously installed, use the Logical Volume Manager 18 | (LVM) to create partitions." 19 | scored: true 20 | 21 | - id: 1.2 22 | description: "Ensure the container host has been Hardened (Not Scored)" 23 | type: manual 24 | remediation: "You may consider various CIS Security Benchmarks for your container host. 25 | If you have other security guidelines or regulatory requirements to adhere to, please 26 | follow them as suitable in your environment. 27 | Additionally, you can run a kernel with grsecurity and PaX . This would add many safety 28 | checks, both at compile-time and run-time. It is also designed to defeat many exploits and 29 | has powerful security features. These features do not require Docker-specific 30 | configuration, since those security features apply system-wide, independent of containers." 31 | scored: false 32 | 33 | - id: 1.3 34 | description: "Ensure Docker is up to date (Not Scored)" 35 | audit: "docker version" 36 | type: manual 37 | remediation: "Keep a track of Docker releases and update as necessary." 38 | scored: false 39 | 40 | - id: 1.4 41 | description: "Ensure only trusted users are allowed to control Docker daemon (Scored)" 42 | audit: "getent group docker" 43 | type: manual 44 | remediation: "Remove any users from the docker group that are not trusted. Additionally, 45 | do not create a mapping of sensitive directories on host to container volumes." 46 | scored: true 47 | 48 | - id: 1.5 49 | description: "Ensure auditing is configured for the docker daemon (Scored)" 50 | audit: "auditctl -l | grep /usr/bin/docker" 51 | tests: 52 | test_items: 53 | - flag: "/usr/bin/docker" 54 | set: true 55 | remediation: "Add a rule for Docker daemon. 56 | For example, 57 | Add the line as below line in /etc/audit/audit.rules file:\n 58 | -w /usr/bin/docker -k docker\n 59 | Then, restart the audit daemon. For example,\n 60 | service auditd restart" 61 | scored: true 62 | 63 | - id: 1.6 64 | description: "Ensure auditing is configured for Docker files and directories - 65 | $docker-storage (Scored)" 66 | audit: "auditctl -l | grep $docker-storage" 67 | tests: 68 | test_items: 69 | - flag: "$docker-storage" 70 | set: true 71 | remediation: "Add a rule for $docker-storage directory. 72 | For example, 73 | Add the line as below in /etc/audit/audit.rules file:\n 74 | -w $docker-storage -k docker\n 75 | Then, restart the audit daemon. For example,\n 76 | service auditd restart" 77 | scored: true 78 | 79 | - id: 1.7 80 | description: "Ensure auditing is configured for Docker files and directories - 81 | /etc/docker (Scored)" 82 | audit: "auditctl -l | grep /etc/docker" 83 | tests: 84 | test_items: 85 | - flag: "/etc/docker" 86 | set: true 87 | remediation: "Add a rule for /etc/docker directory. 88 | For example, 89 | Add the line as below in /etc/audit/audit.rules file:\n 90 | -w /etc/docker -k docker\n 91 | Then, restart the audit daemon. For example,\n 92 | service auditd restart" 93 | scored: true 94 | 95 | - id: 1.8 96 | description: "Ensure auditing is configured for Docker files and directories - 97 | docker.service (Scored)" 98 | audit: "systemctl show -p FragmentPath docker.service | awk -F \"=\" '{print $2}' |xargs -I % test -f % && auditctl -l | grep `systemctl show -p FragmentPath docker.service | awk -F \"=\" '{print $2}'`" 99 | tests: 100 | test_items: 101 | - flag: "docker.service" 102 | set: true 103 | remediation: "If the file exists, add a rule for it. 104 | For example, 105 | Add the line as below in /etc/audit/audit.rules file:\n 106 | -w /usr/lib/systemd/system/docker.service -k docker\n 107 | Then, restart the audit daemon. For example,\n 108 | service auditd restart" 109 | scored: true 110 | 111 | - id: 1.9 112 | description: "Ensure auditing is configured for Docker files and directories - 113 | docker.socket (Scored)" 114 | audit: "systemctl show -p FragmentPath docker.socket | awk -F \"=\" '{print $2}' |xargs -I % test -f % && auditctl -l | grep `systemctl show -p FragmentPath docker.socket | awk -F \"=\" '{print $2}'`" 115 | tests: 116 | test_items: 117 | - flag: "docker.socket" 118 | set: true 119 | remediation: "If the file exists, add a rule for it. 120 | For example, 121 | Add the line as below in /etc/audit/audit.rules file:\n 122 | -w /usr/lib/systemd/system/docker.socket -k docker\n 123 | Then, restart the audit daemon. For example,\n 124 | service auditd restart" 125 | scored: true 126 | 127 | - id: 1.10 128 | description: "Ensure auditing is configured for Docker files and directories - 129 | /etc/default/docker (Scored)" 130 | audit: "auditctl -l | grep /etc/default/docker" 131 | tests: 132 | test_items: 133 | - flag: "/etc/default/docker" 134 | set: true 135 | remediation: "Add a rule for /etc/default/docker file. 136 | For example, 137 | Add the line as below in /etc/audit/audit.rules file:\n 138 | -w /etc/default/docker -k docker\n 139 | Then, restart the audit daemon. For example,\n 140 | service auditd restart" 141 | scored: true 142 | 143 | - id: 1.11 144 | description: "Ensure auditing is configured for Docker files and directories - 145 | /etc/docker/daemon.json (Scored)" 146 | audit: "auditctl -l | grep /etc/docker/daemon.json" 147 | tests: 148 | test_items: 149 | - flag: "/etc/docker/daemon.json" 150 | set: true 151 | remediation: "Add a rule for /etc/docker/daemon.json file. 152 | For example, 153 | Add the line as below in /etc/audit/audit.rules file:\n 154 | -w /etc/docker/daemon.json -k docker\n 155 | Then, restart the audit daemon. For example,\n 156 | service auditd restart" 157 | scored: true 158 | 159 | - id: 1.12 160 | description: "Ensure auditing is configured for Docker files and directories - 161 | /usr/bin/docker-containerd (Scored)" 162 | audit: "auditctl -l | grep /usr/bin/docker-containerd" 163 | tests: 164 | test_items: 165 | - flag: "/usr/bin/docker-containerd" 166 | set: true 167 | remediation: "Add a rule for /usr/bin/docker-containerd file. 168 | For example, 169 | Add the line as below in /etc/audit/audit.rules file:\n 170 | -w /usr/bin/docker-containerd -k docker\n 171 | Then, restart the audit daemon. For example,\n 172 | service auditd restart" 173 | scored: true 174 | 175 | - id: 1.13 176 | description: "Ensure auditing is configured for Docker files and directories - 177 | /usr/bin/docker-runc (Scored)" 178 | audit: "auditctl -l | grep /usr/bin/docker-runc" 179 | tests: 180 | test_items: 181 | - flag: "/usr/bin/docker-runc" 182 | set: true 183 | remediation: "Add a rule for /usr/bin/docker-runc file. 184 | For example, 185 | Add the line as below in /etc/audit/audit.rules file:\n 186 | -w /usr/bin/docker-runc -k docker\n 187 | Then, restart the audit daemon. For example,\n 188 | service auditd restart" 189 | scored: true 190 | 191 | - id: 2 192 | description: "Docker daemon configuration" 193 | checks: 194 | - id: 2.1 195 | description: "Ensure network traffic is restricted between containers on the 196 | default bridge (Scored)" 197 | audit: "docker network ls --quiet | xargs docker network inspect --format '{{ .Name }}: {{ .Options }}'" 198 | tests: 199 | test_items: 200 | - flag: "com.docker.network.bridge.enable_icc:true" 201 | set: false 202 | remediation: | 203 | Run the docker in daemon mode and pass --icc=false as an argument. 204 | For Example, 205 | dockerd --icc=false 206 | Alternatively, you can follow the Docker documentation and create a custom network and 207 | only join containers that need to communicate to that custom network. The --icc 208 | parameter only applies to the default docker bridge, if custom networks are used then the 209 | approach of segmenting networks should be adopted instead. 210 | scored: true 211 | 212 | - id: 2.2.a 213 | description: "Ensure the logging level is set to 'info' (Scored)" 214 | audit: "ps -ef | grep docker" 215 | tests: 216 | bin_op: or 217 | test_items: 218 | - flag: "--log-level" 219 | set: false 220 | - flag: "--log-level" 221 | compare: 222 | op: eq 223 | value: "info" 224 | set: true 225 | remediation: | 226 | Ensure that the Docker daemon configuration file has the following configuration included 227 | "log-level": "info" 228 | Alternatively, run the Docker daemon as below: 229 | dockerd --log-level="info" 230 | scored: true 231 | 232 | - id: 2.2.b 233 | description: "Ensure the logging level is set to 'info' (Scored)" 234 | audit: "grep log-level /etc/docker/daemon.json" 235 | tests: 236 | test_items: 237 | - flag: "\"log-level\"" 238 | compare: 239 | op: eq 240 | value: "info" 241 | set: true 242 | remediation: | 243 | Ensure that the Docker daemon configuration file has the following configuration included 244 | "log-level": "info" 245 | Alternatively, run the Docker daemon as below: 246 | dockerd --log-level="info" 247 | scored: true 248 | 249 | - id: 2.3 250 | description: "Ensure Docker is allowed to make changes to iptables (Scored)" 251 | audit: "ps -ef | grep dockerd" 252 | tests: 253 | bin_op: or 254 | test_items: 255 | - flag: "--iptables" 256 | set: false 257 | - flag: "--iptables" 258 | compare: 259 | op: eq 260 | value: true 261 | set: true 262 | remediation: | 263 | Do not run the Docker daemon with --iptables=false parameter. For example, do not 264 | start the Docker daemon as below: 265 | dockerd --iptables=false 266 | scored: true 267 | 268 | - id: 2.4 269 | description: "Ensure insecure registries are not used (Scored)" 270 | audit: "ps -ef | grep dockerd" 271 | tests: 272 | test_items: 273 | - flag: "--insecure-registry" 274 | set: false 275 | remediation: | 276 | Do not use any insecure registries. 277 | For example, do not start the Docker daemon as below: 278 | dockerd --insecure-registry 10.1.0.0/16 279 | scored: true 280 | 281 | - id: 2.5 282 | description: "Ensure aufs storage driver is not used (Scored)" 283 | audit: docker info | grep -e "^Storage Driver:\s*aufs\s*$" 284 | tests: 285 | test_items: 286 | - flag: "Storage Driver: aufs" 287 | set: false 288 | remediation: | 289 | Do not explicitly use aufs as storage driver. 290 | For example, do not start Docker daemon as below: 291 | dockerd --storage-driver aufs 292 | scored: true 293 | 294 | - id: 2.6.a 295 | description: "Ensure TLS authentication for Docker daemon is configured (Scored)" 296 | audit: "ps -ef | grep dockerd" 297 | tests: 298 | bin_op: and 299 | test_items: 300 | - flag: "--tlsverify" 301 | set: true 302 | - flag: "--tlscacert" 303 | set: true 304 | - flag: "--tlscert" 305 | set: true 306 | - flag: "--tlskey" 307 | set: true 308 | remediation: | 309 | Follow the steps mentioned in the Docker documentation or other references. 310 | scored: true 311 | 312 | - id: 2.6.b 313 | description: "Ensure TLS authentication for Docker daemon is configured (Scored)" 314 | audit: 'grep tls /etc/docker/daemon.json | tr -d ","' 315 | tests: 316 | bin_op: and 317 | test_items: 318 | - flag: '"tlsverify"' 319 | compare: 320 | op: eq 321 | value: true 322 | set: true 323 | - flag: '"tlscacert"' 324 | compare: 325 | op: noteq 326 | value: '""' 327 | set: true 328 | - flag: '"tlscert"' 329 | compare: 330 | op: noteq 331 | value: '""' 332 | set: true 333 | - flag: '"tlskey"' 334 | compare: 335 | op: noteq 336 | value: '""' 337 | set: true 338 | remediation: | 339 | Follow the steps mentioned in the Docker documentation or other references. 340 | scored: true 341 | 342 | - id: 2.7 343 | description: "Ensure the default ulimit is configured appropriately (Not Scored)" 344 | audit: ps -ef | grep dockerd 345 | type: manual 346 | tests: 347 | test_items: 348 | - flag: "--default-ulimit" 349 | set: true 350 | remediation: | 351 | Run the docker in daemon mode and pass --default-ulimit as argument with respective 352 | ulimits as appropriate in your environment. 353 | For Example, 354 | dockerd --default-ulimit nproc=1024:2048 --default-ulimit nofile=100:200 355 | scored: false 356 | 357 | - id: 2.8 358 | description: "Enable user namespace support (Scored)" 359 | audit: docker info --format '{{ .SecurityOptions }}' 360 | tests: 361 | test_items: 362 | - flag: "userns" 363 | compare: 364 | op: has 365 | value: "userns" 366 | set: true 367 | remediation: | 368 | Please consult Docker documentation for various ways in which this can be configured 369 | depending upon your requirements. Your steps might also vary based on platform - For 370 | example, on Red Hat, sub-UIDs and sub-GIDs mapping creation does not work 371 | automatically. You might have to create your own mapping. 372 | However, the high-level steps are as below: 373 | Step 1: Ensure that the files /etc/subuid and /etc/subgid exist. 374 | touch /etc/subuid /etc/subgid 375 | Step 2: Start the docker daemon with --userns-remap flag 376 | dockerd --userns-remap=default 377 | scored: true 378 | 379 | - id: 2.9.a 380 | description: "Ensure the default cgroup usage has been confirmed (Scored)" 381 | audit: ps -ef | grep dockerd 382 | tests: 383 | bin_op: or 384 | test_items: 385 | - flag: "--cgroup-parent" 386 | set: false 387 | - flag: "--cgroup-parent" 388 | compare: 389 | op: nothave 390 | value: "/docker" 391 | set: true 392 | remediation: | 393 | The default setting is good enough and can be left as-is. If you want to specifically set a non- 394 | default cgroup, pass --cgroup-parent parameter to the docker daemon when starting it. 395 | For Example, 396 | dockerd --cgroup-parent=/foobar 397 | scored: true 398 | 399 | - id: 2.9.b 400 | description: "Ensure the default cgroup usage has been confirmed (Scored)" 401 | audit: "grep cgroup-parent /etc/docker/daemon.json" 402 | tests: 403 | bin_op: or 404 | test_items: 405 | - flag: "" 406 | compare: 407 | op: eq 408 | value: "" 409 | set: false 410 | - flag: '"cgroup-parent"' 411 | compare: 412 | op: nothave 413 | value: "/docker" 414 | set: true 415 | remediation: | 416 | The default setting is good enough and can be left as-is. If you want to specifically set a non- 417 | default cgroup, pass --cgroup-parent parameter to the docker daemon when starting it. 418 | For Example, 419 | dockerd --cgroup-parent=/foobar 420 | scored: true 421 | 422 | - id: 2.10.a 423 | description: "Ensure base device size is not changed until needed (Scored)" 424 | audit: ps -ef | grep dockerd 425 | tests: 426 | test_items: 427 | - flag: "--storage-opt" 428 | set: false 429 | remediation: | 430 | Do not set --storage-opt dm.basesize until needed. 431 | scored: true 432 | 433 | - id: 2.10.b 434 | description: "Ensure base device size is not changed until needed (Scored)" 435 | audit: "grep storage-opt /etc/docker/daemon.json" 436 | tests: 437 | test_items: 438 | - flag: '"storage-opt"' 439 | compare: 440 | op: eq 441 | value: "" 442 | set: true 443 | remediation: | 444 | Do not set --storage-opt dm.basesize until needed. 445 | scored: true 446 | 447 | - id: 2.11.a 448 | description: "Ensure that authorization for Docker client commands is enabled (Scored)" 449 | audit: ps -ef | grep dockerd 450 | tests: 451 | test_items: 452 | - flag: "--authorization-plugin" 453 | set: true 454 | remediation: | 455 | Step 1: Install/Create an authorization plugin. 456 | Step 2: Configure the authorization policy as desired. 457 | Step 3: Start the docker daemon as below: 458 | dockerd --authorization-plugin= 459 | scored: true 460 | 461 | - id: 2.11.b 462 | description: "Ensure that authorization for Docker client commands is enabled (Scored)" 463 | audit: 'grep authorization-plugin /etc/docker/daemon.json | tr -d ","' 464 | tests: 465 | test_items: 466 | - flag: '"authorization-plugins"' 467 | compare: 468 | op: noteq 469 | value: "[]" 470 | set: true 471 | remediation: | 472 | Step 1: Install/Create an authorization plugin. 473 | Step 2: Configure the authorization policy as desired. 474 | Step 3: Start the docker daemon as below: 475 | dockerd --authorization-plugin= 476 | scored: true 477 | 478 | - id: 2.12 479 | description: "Ensure centralized and remote logging is configured (Scored)" 480 | audit: ps -ef | grep dockerd 481 | type: manual 482 | tests: 483 | test_items: 484 | - flag: "--log-driver" 485 | set: true 486 | remediation: | 487 | Step 1: Setup the desired log driver by following its documentation. 488 | Step 2: Start the docker daemon with that logging driver. 489 | For example, 490 | dockerd --log-driver=syslog --log-opt syslog-address=tcp://192.xxx.xxx.xxx 491 | scored: true 492 | 493 | - id: 2.13 494 | description: "Ensure operations on legacy registry (v1) are Disabled (Scored)" 495 | audit: ps -ef | grep dockerd 496 | tests: 497 | test_items: 498 | - flag: "--disable-legacy-registry" 499 | set: true 500 | remediation: | 501 | Start the docker daemon as below: 502 | dockerd --disable-legacy-registry 503 | scored: true 504 | 505 | - id: 2.14.a 506 | description: "Ensure live restore is Enabled (Scored)" 507 | audit: ps -ef | grep dockerd 508 | tests: 509 | test_items: 510 | - flag: "--live-restore" 511 | set: true 512 | remediation: | 513 | Run the docker in daemon mode and pass --live-restore as an argument. 514 | For Example, 515 | dockerd --live-restore 516 | scored: true 517 | 518 | - id: 2.14.b 519 | description: "Ensure live restore is Enabled (Scored)" 520 | audit: 'grep live-restore /etc/docker/daemon.json | tr -d ","' 521 | tests: 522 | test_items: 523 | - flag: '"live-restore"' 524 | compare: 525 | op: eq 526 | value: "true" 527 | set: true 528 | remediation: | 529 | Run the docker in daemon mode and pass --live-restore as an argument. 530 | For Example, 531 | dockerd --live-restore 532 | scored: true 533 | 534 | - id: 2.15.a 535 | description: "Ensure Userland Proxy is Disabled (Scored)" 536 | audit: "ps -ef | grep dockerd" 537 | tests: 538 | test_items: 539 | - flag: "--userland-proxy" 540 | compare: 541 | op: eq 542 | value: "false" 543 | set: true 544 | remediation: | 545 | Run the Docker daemon as below: 546 | dockerd --userland-proxy=false 547 | scored: true 548 | 549 | - id: 2.15.b 550 | description: "Disable Userland Proxy (Scored)" 551 | audit: 'grep userland-proxy /etc/docker/daemon.json | tr -d ","' 552 | tests: 553 | test_items: 554 | - flag: '"userland-proxy"' 555 | compare: 556 | op: eq 557 | value: "false" 558 | set: true 559 | remediation: | 560 | Run the Docker daemon as below: 561 | dockerd --userland-proxy=false 562 | scored: true 563 | 564 | - id: 2.16 565 | description: "Ensure daemon-wide custom seccomp profile is applied, if needed 566 | (Not Scored)" 567 | audit: docker info --format '{{ .SecurityOptions }}' 568 | tests: 569 | test_items: 570 | - flag: "profile" 571 | compare: 572 | op: nothave 573 | value: "default" 574 | set: true 575 | remediation: | 576 | By default, Docker's default seccomp profile is applied. If this is good for your environment, 577 | no action is necessary. Alternatively, if you choose to apply your own seccomp profile, use 578 | the --seccomp-profile flag at daemon start or put it in the daemon runtime parameters 579 | file. 580 | dockerd --seccomp-profile 581 | scored: false 582 | 583 | - id: 2.17 584 | description: "Ensure experimental features are avoided in production (Scored)" 585 | audit: docker version --format '{{ .Server.Experimental }}' 586 | tests: 587 | test_items: 588 | - flag: "false" 589 | compare: 590 | op: eq 591 | value: "false" 592 | set: true 593 | remediation: | 594 | Do not pass --experimental as a runtime parameter to the docker daemon. 595 | scored: true 596 | 597 | - id: 2.18.a 598 | description: "Ensure containers are restricted from acquiring new privileges (Scored)" 599 | audit: ps -ef | grep dockerd 600 | tests: 601 | test_items: 602 | - flag: "--no-new-privileges" 603 | compare: 604 | op: eq 605 | value: "true" 606 | set: true 607 | remediation: | 608 | Run the Docker daemon as below: 609 | dockerd --no-new-privileges 610 | scored: true 611 | 612 | - id: 2.18.b 613 | description: "Ensure containers are restricted from acquiring new privileges (Scored)" 614 | audit: 'grep no-new-privileges /etc/docker/daemon.json | tr -d ","' 615 | tests: 616 | test_items: 617 | - flag: '"no-new-privileges"' 618 | compare: 619 | op: eq 620 | value: "true" 621 | set: true 622 | remediation: | 623 | For example, you should start your container as below: 624 | docker run --rm -it --security-opt=no-new-privileges ubuntu bash 625 | scored: true 626 | 627 | - id: 3 628 | description: "Docker daemon configuration files" 629 | checks: 630 | - id: 3.1 631 | description: "Ensure that docker.service file ownership is set to root:root (Scored)" 632 | audit: systemctl show -p FragmentPath docker.service | cut -d= -f2 | xargs stat -c %U:%G 633 | tests: 634 | test_items: 635 | - flag: "root:root" 636 | compare: 637 | op: eq 638 | value: "root:root" 639 | set: true 640 | remediation: | 641 | Step 1: Find out the file location: 642 | systemctl show -p FragmentPath docker.service 643 | Step 2: If the file does not exist, this recommendation is not applicable. If the file exists, 644 | execute the below command with the correct file path to set the ownership and group 645 | ownership for the file to root . 646 | For example, 647 | chown root:root /usr/lib/systemd/system/docker.service 648 | scored: true 649 | 650 | - id: 3.2 651 | description: "Ensure that docker.service file permissions are set to 644 or more 652 | restrictive (Scored)" 653 | audit: systemctl show -p FragmentPath docker.service | cut -d= -f2 | xargs stat -c "permissions=%a" 654 | tests: 655 | test_items: 656 | - flag: "permissions" 657 | compare: 658 | op: bitmask 659 | value: "644" 660 | set: true 661 | remediation: | 662 | Step 1: Find out the file location: 663 | systemctl show -p FragmentPath docker.service 664 | Step 2: If the file does not exist, this recommendation is not applicable. If the file exists, 665 | execute the below command with the correct file path to set the file permissions to 644 . 666 | For example, 667 | chmod 644 /usr/lib/systemd/system/docker.service 668 | scored: true 669 | 670 | - id: 3.3 671 | description: "Ensure that docker.socket file ownership is set to root:root (Scored)" 672 | audit: systemctl show -p FragmentPath docker.socket | cut -d= -f2 | xargs stat -c %U:%G 673 | tests: 674 | test_items: 675 | - flag: "root:root" 676 | compare: 677 | op: eq 678 | value: "root:root" 679 | set: true 680 | remediation: | 681 | Step 1: Find out the file location: 682 | systemctl show -p FragmentPath docker.socket 683 | Step 2: If the file does not exist, this recommendation is not applicable. If the file exists, 684 | execute the below command with the correct file path to set the ownership and group 685 | ownership for the file to root . 686 | For example, 687 | chown root:root /usr/lib/systemd/system/docker.socket 688 | scored: true 689 | 690 | - id: 3.4 691 | description: "Ensure that docker.socket file permissions are set to 644 or more 692 | restrictive (Scored)" 693 | audit: systemctl show -p FragmentPath docker.socket | cut -d= -f2 | xargs stat -c "permissions=%a" 694 | tests: 695 | test_items: 696 | - flag: "permissions" 697 | compare: 698 | op: bitmask 699 | value: "644" 700 | set: true 701 | remediation: | 702 | Step 1: Find out the file location: 703 | systemctl show -p FragmentPath docker.socket 704 | Step 2: If the file does not exist, this recommendation is not applicable. If the file exists, 705 | execute the below command with the correct file path to set the file permissions to 644 . 706 | For example, 707 | chmod 644 /usr/lib/systemd/system/docker.socket 708 | scored: true 709 | 710 | - id: 3.5 711 | description: "Ensure that /etc/docker directory ownership is set to root:root 712 | (Scored)" 713 | audit: stat -c %U:%G /etc/docker 714 | tests: 715 | test_items: 716 | - flag: "root:root" 717 | compare: 718 | op: eq 719 | value: "root:root" 720 | set: true 721 | remediation: | 722 | chown root:root /etc/docker 723 | This would set the ownership and group-ownership for the directory to root . 724 | scored: true 725 | 726 | - id: 3.6 727 | description: "Ensure that /etc/docker directory permissions are set to 755 or more 728 | restrictive (Scored)" 729 | audit: stat -c "permissions=%a" /etc/docker 730 | tests: 731 | test_items: 732 | - flag: "permissions" 733 | compare: 734 | op: bitmask 735 | value: "755" 736 | set: true 737 | remediation: | 738 | chmod 755 /etc/docker 739 | This would set the permissions for the directory to 755 . 740 | scored: true 741 | 742 | - id: 3.7 743 | description: "Ensure that registry certificate file ownership is set to root:root 744 | (Scored)" 745 | audit: stat -c %U:%G /etc/docker/certs.d/* 746 | tests: 747 | test_items: 748 | - flag: "root:root" 749 | set: true 750 | remediation: | 751 | chown root:root /etc/docker/certs.d//* 752 | This would set the ownership and group-ownership for the registry certificate files to root . 753 | scored: true 754 | 755 | - id: 3.8 756 | description: "Ensure that registry certificate file permissions are set to 444 or 757 | more restrictive (Scored)" 758 | audit: stat -c "permissions=%a" /etc/docker/certs.d/* 759 | tests: 760 | test_items: 761 | - flag: "permissions" 762 | compare: 763 | op: bitmask 764 | value: "444" 765 | set: true 766 | remediation: | 767 | chmod 444 /etc/docker/certs.d//* 768 | This would set the permissions for registry certificate files to 444 . 769 | scored: true 770 | 771 | - id: 3.9 772 | description: "Ensure that TLS CA certificate file ownership is set to root:root 773 | (Scored)" 774 | type: manual 775 | remediation: | 776 | chown root:root 777 | This would set the ownership and group-ownership for the TLS CA certificate file to root . 778 | scored: true 779 | 780 | - id: 3.10 781 | description: "Ensure that TLS CA certificate file permissions are set to 444 or 782 | more restrictive (Scored)" 783 | type: manual 784 | remediation: | 785 | chmod 444 786 | This would set the file permissions of the TLS CA file to 444 . 787 | scored: true 788 | 789 | - id: 3.11 790 | description: "Ensure that Docker server certificate file ownership is set to 791 | root:root (Scored)" 792 | type: manual 793 | remediation: | 794 | chown root:root 795 | This would set the ownership and group-ownership for the Docker server certificate file to 796 | root . 797 | scored: true 798 | 799 | - id: 3.12 800 | description: "Ensure that Docker server certificate file permissions are set to 444 801 | or more restrictive (Scored)" 802 | type: manual 803 | remediation: | 804 | chmod 444 805 | This would set the file permissions of the Docker server file to 444 . 806 | scored: true 807 | 808 | - id: 3.13 809 | description: "Ensure that Docker server certificate key file ownership is set to 810 | root:root (Scored)" 811 | type: manual 812 | remediation: | 813 | chown root:root 814 | This would set the ownership and group-ownership for the Docker server certificate key 815 | file to root. 816 | scored: true 817 | 818 | - id: 3.14 819 | description: "Ensure that Docker server certificate key file permissions are set to 820 | 400 (Scored)" 821 | type: manual 822 | remediation: | 823 | chmod 400 824 | This would set the Docker server certificate key file permissions to 400 . 825 | scored: true 826 | 827 | - id: 3.15 828 | description: "Ensure that Docker socket file ownership is set to root:docker 829 | (Scored)" 830 | audit: stat -c %U:%G /var/run/docker.sock 831 | tests: 832 | test_items: 833 | - flag: "root:docker" 834 | compare: 835 | op: eq 836 | value: "root:docker" 837 | set: true 838 | remediation: | 839 | chown root:docker /var/run/docker.sock 840 | This would set the ownership to root and group-ownership to docker for default Docker 841 | socket file. 842 | scored: true 843 | 844 | - id: 3.16 845 | description: "Ensure that Docker socket file permissions are set to 660 or more 846 | restrictive (Scored)" 847 | audit: stat -c "permissions=%a" /var/run/docker.sock 848 | tests: 849 | test_items: 850 | - flag: "permissions" 851 | compare: 852 | op: bitmask 853 | value: "660" 854 | set: true 855 | remediation: | 856 | chmod 660 /var/run/docker.sock 857 | This would set the file permissions of the Docker socket file to 660 . 858 | scored: true 859 | 860 | - id: 3.17 861 | description: "Ensure that daemon.json file ownership is set to root:root (Scored)" 862 | audit: /bin/sh -c "if [ -f /etc/docker/daemon.json ]; then stat -c %U:%G /etc/docker/daemon.json; fi" 863 | tests: 864 | test_items: 865 | - flag: "root:root" 866 | compare: 867 | op: eq 868 | value: "root:root" 869 | set: true 870 | remediation: | 871 | chown root:root /etc/docker/daemon.json 872 | This would set the ownership and group-ownership for the file to root . 873 | scored: true 874 | 875 | - id: 3.18 876 | description: "Ensure that daemon.json file permissions are set to 644 or more 877 | restrictive (Scored)" 878 | audit: /bin/sh -c "if [ -f /etc/docker/daemon.json ]; then stat -c "permissions=%a" /etc/docker/daemon.json; fi" 879 | tests: 880 | test_items: 881 | - flag: "permissions" 882 | compare: 883 | op: bitmask 884 | value: "644" 885 | set: true 886 | remediation: | 887 | chown root:root /etc/default/docker 888 | This would set the ownership and group-ownership for the file to root . 889 | scored: true 890 | 891 | - id: 3.19 892 | description: "Ensure that /etc/default/docker file ownership is set to root:root 893 | (Scored)" 894 | audit: /bin/sh -c "if [ -f /etc/default/docker ]; then stat -c %U:%G /etc/default/docker; fi" 895 | tests: 896 | test_items: 897 | - flag: "root:root" 898 | compare: 899 | op: eq 900 | value: "root:root" 901 | set: true 902 | remediation: | 903 | chown root:root /etc/default/docker 904 | This would set the ownership and group-ownership for the file to root . 905 | scored: true 906 | 907 | - id: 3.20 908 | description: "Ensure that /etc/default/docker file permissions are set to 644 or 909 | more restrictive (Scored)" 910 | audit: /bin/sh -c "if [ -f /etc/default/docker ]; then stat -c "permissions=%a" /etc/default/docker; fi" 911 | tests: 912 | test_items: 913 | - flag: "permissions" 914 | compare: 915 | op: bitmask 916 | value: "644" 917 | set: true 918 | remediation: | 919 | chmod 644 /etc/default/docker 920 | This would set the file permissions for this file to 644. 921 | scored: true 922 | 923 | - id: 4 924 | description: "Container Images and Build File" 925 | checks: 926 | - id: 4.1 927 | description: "Ensure a user for the container has been created (Scored)" 928 | audit: "docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}: User={{ .Config.User }}'" 929 | use_multiple_values: true 930 | tests: 931 | bin_op: and 932 | test_items: 933 | - flag: "User" 934 | compare: 935 | op: nothave 936 | value: "root" 937 | set: true 938 | - flag: "User" 939 | compare: 940 | op: noteq 941 | value: "" 942 | set: true 943 | - flag: "User" 944 | compare: 945 | op: noteq 946 | value: "1" 947 | set: true 948 | remediation: | 949 | Ensure that the Dockerfile for the container image contains below instruction: 950 | USER 951 | where username or ID refers to the user that could be found in the container base image. If 952 | there is no specific user created in the container base image, then add a useradd command 953 | to add the specific user before USER instruction. 954 | For example, add the below lines in the Dockerfile to create a user in the container: 955 | RUN useradd -d /home/username -m -s /bin/bash username 956 | USER username 957 | Note: If there are users in the image that the containers do not need, consider deleting 958 | them. After deleting those users, commit the image and then generate new instances of 959 | containers for use. 960 | scored: true 961 | 962 | - id: 4.2 963 | description: "Ensure that containers use trusted base images (Not Scored)" 964 | type: manual 965 | remediation: | 966 | - Configure and use Docker Content trust. 967 | - Inspect Docker image history to evaluate their risk to operate on your network. 968 | - Scan Docker images for vulnerabilities in their dependencies and configurations 969 | they will impose upon your network. 970 | scored: false 971 | 972 | - id: 4.3 973 | description: "Ensure unnecessary packages are not installed in the container (Not Scored)" 974 | type: manual 975 | remediation: | 976 | At the outset, do not install anything on the container that does not justify the purpose. If 977 | the image had some packages that your container does not use, uninstall them. 978 | Consider using a minimal base image rather than the standard Redhat/Centos/Debian 979 | images if you can. Some of the options include BusyBox and Alpine. 980 | Not only does this trim your image size from >150Mb to ~20 Mb, there are also fewer tools 981 | and paths to escalate privileges. You can even remove the package installer as a final 982 | hardening measure for leaf/production containers. 983 | scored: false 984 | 985 | - id: 4.4 986 | description: "Ensure images are scanned and rebuilt to include security patches 987 | (Not Scored)" 988 | type: manual 989 | remediation: | 990 | Follow the below steps to rebuild the images with security patches: 991 | Step 1: Pull all the base images (i.e., given your set of Dockerfiles, extract all images 992 | declared in FROM instructions, and re-pull them to check for an updated/patched versions). 993 | Patch the packages within the images too. 994 | docker pull 995 | Step 2: Force a rebuild of each image: 996 | docker build --no-cache 997 | Step 3: Restart all containers with the updated images. 998 | You could also use ONBUILD directive in the Dockerfile to trigger particular update 999 | instructions for images that you know are used as base images frequently. 1000 | scored: false 1001 | 1002 | - id: 4.5 1003 | description: "Ensure Content trust for Docker is Enabled (Scored)" 1004 | audit: echo $DOCKER_CONTENT_TRUST 1005 | tests: 1006 | test_items: 1007 | - flag: 1 1008 | compare: 1009 | op: has 1010 | value: 1 1011 | set: true 1012 | remediation: | 1013 | To enable content trust in a bash shell, enter the following command: 1014 | export DOCKER_CONTENT_TRUST=1 1015 | Alternatively, set this environment variable in your profile file so that content trust in 1016 | enabled on every login. 1017 | scored: true 1018 | 1019 | - id: 4.6 1020 | description: "Ensure HEALTHCHECK instructions have been added to the container 1021 | image (Scored)" 1022 | type: manual 1023 | remediation: | 1024 | Follow Docker documentation and rebuild your container image with HEALTHCHECK 1025 | instruction. 1026 | scored: true 1027 | 1028 | - id: 4.7 1029 | description: "Ensure update instructions are not use alone in the Dockerfile (Not 1030 | Scored)" 1031 | type: manual 1032 | remediation: | 1033 | Use update instructions along with install instructions (or any other) and version pinning 1034 | for packages while installing them. This would bust the cache and force to extract the 1035 | required versions. 1036 | Alternatively, you could use --no-cache flag during docker build process to avoid using 1037 | cached layers. 1038 | scored: false 1039 | 1040 | - id: 4.8 1041 | description: "Ensure setuid and setgid permissions are removed in the images 1042 | (Not Scored)" 1043 | type: manual 1044 | remediation: | 1045 | Allow setuid and setgid permissions only on executables which need them. You could 1046 | remove these permissions during build time by adding the following command in your 1047 | Dockerfile, preferably towards the end of the Dockerfile: 1048 | RUN find / -perm +6000 -type f -exec chmod a-s {} \; || true 1049 | scored: false 1050 | 1051 | - id: 4.9 1052 | description: "Ensure COPY is used instead of ADD in Dockerfile (Not Scored)" 1053 | type: manual 1054 | remediation: | 1055 | Use COPY instructions in Dockerfiles. 1056 | scored: false 1057 | 1058 | - id: 4.10 1059 | description: "Ensure secrets are not stored in Dockerfiles (Not Scored)" 1060 | type: manual 1061 | remediation: | 1062 | scored: false 1063 | 1064 | - id: 4.11 1065 | description: "Ensure verified packages are only Installed (Not Scored)" 1066 | type: manual 1067 | remediation: | 1068 | Use GPG keys for downloading and verifying packages or any other secure package 1069 | distribution mechanism of your choice. 1070 | scored: false 1071 | 1072 | - id: 5 1073 | description: "Container Runtime" 1074 | checks: 1075 | - id: 5.1 1076 | description: "Ensure AppArmor Profile is Enabled (Scored)" 1077 | type: manual 1078 | remediation: | 1079 | If AppArmor is applicable for your Linux OS, use it. You may have to follow below set of 1080 | steps: 1081 | 1. Verify if AppArmor is installed. If not, install it. 1082 | 2. Create or import a AppArmor profile for Docker containers. 1083 | 3. Put this profile in enforcing mode. 1084 | 4. Start your Docker container using the customized AppArmor profile. For example, 1085 | docker run --interactive --tty --security-opt="apparmor:PROFILENAME" centos 1086 | /bin/bash 1087 | scored: true 1088 | 1089 | - id: 5.2 1090 | description: "Ensure SELinux security options are set, if applicable (Scored)" 1091 | type: manual 1092 | remediation: | 1093 | If SELinux is applicable for your Linux OS, use it. You may have to follow below set of steps: 1094 | 1. Set the SELinux State. 1095 | 2. Set the SELinux Policy. 1096 | 3. Create or import a SELinux policy template for Docker containers. 1097 | 4. Start Docker in daemon mode with SELinux enabled. For example, 1098 | docker daemon --selinux-enabled 1099 | 5. Start your Docker container using the security options. For example, 1100 | docker run --interactive --tty --security-opt label=level:TopSecret centos 1101 | /bin/bash 1102 | scored: true 1103 | 1104 | - id: 5.3 1105 | description: "Ensure Linux Kernel Capabilities are restricted within containers 1106 | (Scored)" 1107 | type: manual 1108 | remediation: | 1109 | Execute the below command to add needed capabilities: 1110 | $> docker run --cap-add={"Capability 1","Capability 2"} 1111 | For example, 1112 | docker run --interactive --tty --cap-add={"NET_ADMIN","SYS_ADMIN"} 1113 | centos:latest /bin/bash 1114 | scored: true 1115 | 1116 | - id: 5.4 1117 | description: "Ensure privileged containers are not used (Scored)" 1118 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:Privileged={{ .HostConfig.Privileged }}' 1119 | use_multiple_values: true 1120 | tests: 1121 | test_items: 1122 | - flag: "Privileged=true" 1123 | set: false 1124 | remediation: | 1125 | Do not run container with the --privileged flag. 1126 | For example, do not start a container as below: 1127 | docker run --interactive --tty --privileged centos /bin/bash 1128 | scored: true 1129 | 1130 | - id: 5.5 1131 | description: "Ensure sensitive host system directories are not mounted on 1132 | containers (Scored)" 1133 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:Volumes={{ .Mounts }}' 1134 | use_multiple_values: true 1135 | tests: 1136 | test_items: 1137 | - flag: "Source:/ Destination" 1138 | set: false 1139 | - flag: "Source:/boot Destination" 1140 | set: false 1141 | - flag: "Source:/dev Destination" 1142 | set: false 1143 | - flag: "Source:/etc Destination" 1144 | set: false 1145 | - flag: "Source:/lib Destination" 1146 | set: false 1147 | - flag: "Source:/proc Destination" 1148 | set: false 1149 | - flag: "Source:/sys Destination" 1150 | set: false 1151 | - flag: "Source:/usr Destination" 1152 | set: false 1153 | remediation: | 1154 | Do not mount host sensitive directories on containers especially in read-write mode. 1155 | scored: true 1156 | 1157 | - id: 5.6 1158 | description: "Ensure ssh is not run within containers (Scored)" 1159 | type: manual 1160 | remediation: | 1161 | Uninstall SSH server from the container and use nsenter or any other commands such as 1162 | docker exec or docker attach to interact with the container instance. 1163 | docker exec --interactive --tty $INSTANCE_ID sh 1164 | OR 1165 | docker attach $INSTANCE_ID 1166 | scored: true 1167 | 1168 | - id: 5.7 1169 | description: "Ensure privileged ports are not mapped within containers (Scored)" 1170 | type: manual 1171 | remediation: | 1172 | Do not map the container ports to privileged host ports when starting a container. Also, 1173 | ensure that there is no such container to host privileged port mapping declarations in the 1174 | Dockerfile. 1175 | scored: true 1176 | 1177 | - id: 5.8 1178 | description: "Ensure only needed ports are open on the container (Scored)" 1179 | type: manual 1180 | remediation: | 1181 | Fix the Dockerfile of the container image to expose only needed ports by your 1182 | containerized application. You can also completely ignore the list of ports defined in the 1183 | Dockerfile by NOT using -P (UPPERCASE) or --publish-all flag when starting the 1184 | container. Use the -p (lowercase) or --publish flag to explicitly define the ports that you 1185 | need for a particular container instance. 1186 | For example, 1187 | docker run --interactive --tty --publish 5000 --publish 5001 --publish 5002 1188 | centos /bin/bash 1189 | scored: true 1190 | 1191 | - id: 5.9 1192 | description: "Ensure the host's network namespace is not shared (Scored)" 1193 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:NetworkMode={{ .HostConfig.NetworkMode }}' 1194 | use_multiple_values: true 1195 | tests: 1196 | test_items: 1197 | - flag: "NetworkMode=host" 1198 | set: false 1199 | remediation: | 1200 | Do not pass --net=host option when starting the container. 1201 | scored: true 1202 | 1203 | - id: 5.10 1204 | description: "Ensure memory usage for container is limited (Scored)" 1205 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:Memory={{ .HostConfig.Memory }}' 1206 | use_multiple_values: true 1207 | tests: 1208 | test_items: 1209 | - flag: "Memory" 1210 | compare: 1211 | op: gt 1212 | value: 0 1213 | set: true 1214 | remediation: | 1215 | Run the container with only as much memory as required. Always run the container using 1216 | the --memory argument. 1217 | For example, you could run a container as below: 1218 | docker run --interactive --tty --memory 256m centos /bin/bash 1219 | In the above example, the container is started with a memory limit of 256 MB. 1220 | Note: Please note that the output of the below command would return values in scientific 1221 | notation if memory limits are in place. 1222 | docker inspect --format='{{.Config.Memory}}' 7c5a2d4c7fe0 1223 | 1224 | For example, if the memory limit is set to 256 MB for the above container instance, the 1225 | output of the above command would be 2.68435456e+08 and NOT 256m. You should 1226 | convert this value using a scientific calculator or programmatic methods. 1227 | scored: true 1228 | 1229 | - id: 5.11 1230 | description: "Ensure CPU priority is set appropriately on the container (Scored)" 1231 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:CpuShares={{ .HostConfig.CpuShares }}' 1232 | use_multiple_values: true 1233 | tests: 1234 | test_items: 1235 | - flag: "CpuShares" 1236 | compare: 1237 | op: gt 1238 | value: 0 1239 | set: true 1240 | - flag: "CpuShares" 1241 | compare: 1242 | op: lt 1243 | value: 1024 1244 | set: true 1245 | remediation: | 1246 | Manage the CPU shares between your containers. To do so start the container using the -- 1247 | cpu-shares argument. 1248 | For example, you could run a container as below: 1249 | docker run --interactive --tty --cpu-shares 512 centos /bin/bash 1250 | In the above example, the container is started with CPU shares of 50% of what the other 1251 | containers use. So, if the other container has CPU shares of 80%, this container will have 1252 | CPU shares of 40%. 1253 | Note: Every new container will have 1024 shares of CPU by default. However, this value is 1254 | shown as 0 if you run the command mentioned in the audit section. 1255 | scored: true 1256 | 1257 | - id: 5.12 1258 | description: "Ensure the container's root filesystem is mounted as read only 1259 | (Scored)" 1260 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:ReadonlyRootfs={{ .HostConfig.ReadonlyRootfs }}' 1261 | use_multiple_values: true 1262 | tests: 1263 | test_items: 1264 | - flag: "ReadonlyRootfs=false" 1265 | set: false 1266 | remediation: | 1267 | Add a --read-only flag at a container's runtime to enforce the container's root filesystem 1268 | to be mounted as read only. 1269 | docker run --read-only 1270 | Enabling the --read-only option at a container's runtime should be used by administrators 1271 | to force a container's executable processes to only write container data to explicit storage 1272 | locations during the container's runtime. 1273 | Examples of explicit storage locations during a container's runtime include, but not limited 1274 | to: 1275 | 1. Use the --tmpfs option to mount a temporary file system for non-persistent data 1276 | writes. 1277 | docker run --interactive --tty --read-only --tmpfs "/run" --tmpfs "/tmp" 1278 | centos /bin/bash 1279 | 2. Enabling Docker rw mounts at a container's runtime to persist container data 1280 | directly on the Docker host filesystem. 1281 | docker run --interactive --tty --read-only -v /opt/app/data:/run/app/data:rw 1282 | centos /bin/bash 1283 | 3. Utilizing Docker shared-storage volume plugins for Docker data volume to persist 1284 | container data. 1285 | docker volume create -d convoy --opt o=size=20GB my-named-volume 1286 | docker run --interactive --tty --read-only -v my-named-volume:/run/app/data 1287 | centos /bin/bash 1288 | 3. Transmitting container data outside of the docker during the container's runtime 1289 | for container data to persist container data. Examples include hosted databases, 1290 | network file shares, and APIs. 1291 | scored: true 1292 | 1293 | - id: 5.13 1294 | description: "Ensure incoming container traffic is binded to a specific host 1295 | interface (Scored)" 1296 | audit: docker ps --quiet | xargs docker inspect --format '{{ .Id }}:Ports={{ .NetworkSettings.Ports }}' 1297 | use_multiple_values: true 1298 | tests: 1299 | test_items: 1300 | - flag: "0.0.0.0" 1301 | set: false 1302 | remediation: | 1303 | Bind the container port to a specific host interface on the desired host port. 1304 | For example, 1305 | docker run --detach --publish 10.2.3.4:49153:80 nginx 1306 | In the example above, the container port 80 is bound to the host port on 49153 and would 1307 | accept incoming connection only from 10.2.3.4 external interface. 1308 | scored: true 1309 | 1310 | - id: 5.14 1311 | description: "Ensure 'on-failure' container restart policy is set to '5' (Scored)" 1312 | audit: "docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}: RestartPolicyName={{ .HostConfig.RestartPolicy.Name }} MaximumRetryCount={{ .HostConfig.RestartPolicy.MaximumRetryCount }}'" 1313 | use_multiple_values: true 1314 | tests: 1315 | bin_op: and 1316 | test_items: 1317 | - flag: "RestartPolicyName" 1318 | compare: 1319 | op: noteq 1320 | value: "always" 1321 | set: true 1322 | - flag: "RestartPolicyName" 1323 | compare: 1324 | op: noteq 1325 | value: "" 1326 | set: true 1327 | - flag: "RestartPolicyName" 1328 | compare: 1329 | op: eq 1330 | value: "no" 1331 | set: true 1332 | remediation: | 1333 | If a container is desired to be restarted of its own, then, for example, you could start the 1334 | container as below: 1335 | docker run --detach --restart=on-failure:5 nginx 1336 | scored: true 1337 | 1338 | - id: 5.15 1339 | description: "Ensure the host's process namespace is not shared (Scored)" 1340 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:PidMode={{ .HostConfig.PidMode }}' 1341 | use_multiple_values: true 1342 | tests: 1343 | test_items: 1344 | - flag: "PidMode=host" 1345 | set: false 1346 | remediation: | 1347 | Do not start a container with --pid=host argument. 1348 | For example, do not start a container as below: 1349 | docker run --interactive --tty --pid=host centos /bin/bash 1350 | scored: true 1351 | 1352 | - id: 5.16 1353 | description: "Ensure the host's IPC namespace is not shared (Scored)" 1354 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:IpcMode={{ .HostConfig.IpcMode }}' 1355 | use_multiple_values: true 1356 | tests: 1357 | test_items: 1358 | - flag: "IpcMode=host" 1359 | set: false 1360 | remediation: | 1361 | Do not start a container with --ipc=host argument. For example, do not start a container 1362 | as below: 1363 | docker run --interactive --tty --ipc=host centos /bin/bash 1364 | scored: true 1365 | 1366 | - id: 5.17 1367 | description: "Ensure host devices are not directly exposed to containers (Not 1368 | Scored)" 1369 | type: manual 1370 | remediation: | 1371 | Do not directly expose the host devices to containers. If at all, you need to expose the host 1372 | devices to containers, use the correct set of permissions: 1373 | For example, do not start a container as below: 1374 | docker run --interactive --tty --device=/dev/tty0:/dev/tty0:rwm -- 1375 | device=/dev/temp_sda:/dev/temp_sda:rwm centos bash 1376 | For example, share the host device with correct permissions: 1377 | docker run --interactive --tty --device=/dev/tty0:/dev/tty0:rw -- 1378 | device=/dev/temp_sda:/dev/temp_sda:r centos bash 1379 | scored: false 1380 | 1381 | - id: 5.18 1382 | description: "Ensure the default ulimit is overwritten at runtime, only if needed 1383 | (Not Scored)" 1384 | type: manual 1385 | remediation: | 1386 | Only override the default ulimit settings if needed. 1387 | For example, to override default ulimit settings start a container as below: 1388 | docker run --ulimit nofile=1024:1024 --interactive --tty centos /bin/bash 1389 | scored: true 1390 | 1391 | - id: 5.19 1392 | description: "Ensure mount propagation mode is not set to shared (Scored)" 1393 | type: manual 1394 | remediation: | 1395 | Do not mount volumes in shared mode propagation. 1396 | For example, do not start container as below: 1397 | docker run --volume=/hostPath:/containerPath:shared 1398 | 1399 | scored: true 1400 | 1401 | - id: 5.20 1402 | description: "Ensure the host's UTS namespace is not shared (Scored)" 1403 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:UTSMode={{ .HostConfig.UTSMode }}' 1404 | use_multiple_values: true 1405 | tests: 1406 | test_items: 1407 | - flag: "UTSMode=host" 1408 | set: false 1409 | remediation: | 1410 | Do not start a container with --uts=host argument. 1411 | For example, do not start a container as below: 1412 | docker run --rm --interactive --tty --uts=host rhel7.2 1413 | scored: true 1414 | 1415 | - id: 5.21 1416 | description: "Ensure the default seccomp profile is not Disabled (Scored)" 1417 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:SecurityOpt={{ .HostConfig.SecurityOpt }}' 1418 | use_multiple_values: true 1419 | tests: 1420 | bin_op: and 1421 | test_items: 1422 | - flag: "SecurityOpt" 1423 | compare: 1424 | op: nothave 1425 | value: "seccomp:unconfined" 1426 | set: true 1427 | - flag: "SecurityOpt" 1428 | compare: 1429 | op: nothave 1430 | value: "seccomp=unconfined" 1431 | set: true 1432 | remediation: | 1433 | By default, seccomp profiles are enabled. You do not need to do anything unless you want 1434 | to modify and use the modified seccomp profile. 1435 | scored: true 1436 | 1437 | - id: 5.22 1438 | description: "Ensure docker exec commands are not used with privileged option 1439 | (Scored)" 1440 | audit: "ausearch -if $audit-log -k docker | grep exec | grep privileged" 1441 | tests: 1442 | test_items: 1443 | - flag: "" 1444 | compare: 1445 | op: eq 1446 | value: "" 1447 | set: true 1448 | remediation: | 1449 | If audit rule for docker file not set\n 1450 | Add the line as below in /etc/audit/audit.rules file:\n 1451 | -w /usr/bin/docker -p rwxa -k docker-daemon\n 1452 | Then, restart the audit daemon.\n 1453 | service auditd restart\n 1454 | Do not use --privileged option in docker exec command. 1455 | scored: true 1456 | 1457 | - id: 5.23 1458 | description: "Ensure docker exec commands are not used with user option 1459 | (Scored)" 1460 | audit: "ausearch -if $audit-log -k docker | grep exec | grep user" 1461 | tests: 1462 | test_items: 1463 | - flag: "" 1464 | compare: 1465 | op: eq 1466 | value: "" 1467 | set: true 1468 | remediation: | 1469 | If audit rule for docker file not set\n 1470 | Add the line as below in /etc/audit/audit.rules file:\n 1471 | -w /usr/bin/docker -p rwxa -k docker-daemon\n 1472 | Then, restart the audit daemon.\n 1473 | service auditd restart\n 1474 | Do not use --user option in docker exec command. 1475 | scored: true 1476 | 1477 | - id: 5.24 1478 | description: "Ensure cgroup usage is confirmed (Scored)" 1479 | type: manual 1480 | remediation: | 1481 | Do not use --cgroup-parent option in docker run command unless needed. 1482 | scored: true 1483 | 1484 | - id: 5.25 1485 | description: "Ensure the container is restricted from acquiring additional 1486 | privileges (Scored)" 1487 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:SecurityOpt={{ .HostConfig.SecurityOpt }}' 1488 | use_multiple_values: true 1489 | tests: 1490 | test_items: 1491 | - flag: "no-new-privileges" 1492 | set: true 1493 | remediation: | 1494 | For example, you should start your container as below: 1495 | docker run --rm -it --security-opt=no-new-privileges ubuntu bash 1496 | scored: true 1497 | 1498 | - id: 5.26 1499 | description: "Ensure container health is checked at runtime (Scored)" 1500 | type: manual 1501 | remediation: | 1502 | Run the container using --health-cmd and the other parameters. 1503 | For example, 1504 | docker run -d --health-cmd='stat /etc/passwd || exit 1' nginx 1505 | scored: true 1506 | 1507 | - id: 5.27 1508 | description: "Ensure docker commands always get the latest version of the 1509 | image (Not Scored)" 1510 | type: manual 1511 | remediation: | 1512 | Use proper version pinning mechanisms (the latest tag which is assigned by default is still 1513 | vulnerable to caching attacks) to avoid extracting the cached older versions. Version 1514 | pinning mechanisms should be used for base images, packages, and entire images too. You 1515 | can customize version pinning rules as per your requirements. 1516 | scored: false 1517 | 1518 | - id: 5.28 1519 | description: "Ensure PIDs cgroup limit is used (Scored)" 1520 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:PidsLimit={{ .HostConfig.PidsLimit }}' 1521 | use_multiple_values: true 1522 | tests: 1523 | test_items: 1524 | - flag: "PidsLimit" 1525 | compare: 1526 | op: gt 1527 | value: 0 1528 | set: true 1529 | remediation: | 1530 | Use --pids-limit flag while launching the container with an appropriate value. 1531 | For example, 1532 | docker run -it --pids-limit 100 1533 | In the above example, the number of processes allowed to run at any given time is set to 1534 | 100. After a limit of 100 concurrently running processes is reached, docker would restrict 1535 | any new process creation. 1536 | scored: true 1537 | 1538 | - id: 5.29 1539 | description: "Ensure Docker's default bridge docker0 is not used (Not Scored)" 1540 | audit: docker network ls --quiet | xargs xargs docker network inspect --format '{{ .Name }}:{{ .Options }}' 1541 | tests: 1542 | test_items: 1543 | - flag: "com.docker.network.bridge.name:docker0" 1544 | set: false 1545 | remediation: | 1546 | Follow Docker documentation and setup a user-defined network. Run all the containers in 1547 | the defined network. 1548 | scored: false 1549 | 1550 | - id: 5.30 1551 | description: "Ensure the host's user namespaces is not shared (Scored)" 1552 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:UsernsMode={{ .HostConfig.UsernsMode }}' 1553 | tests: 1554 | test_items: 1555 | - flag: "UsernsMode" 1556 | compare: 1557 | op: eq 1558 | value: "" 1559 | set: true 1560 | remediation: | 1561 | Do not share user namespaces between host and containers. 1562 | For example, do not run a container as below: 1563 | docker run --rm -it --userns=host ubuntu bash 1564 | scored: true 1565 | 1566 | - id: 5.31 1567 | description: "Ensure the Docker socket is not mounted inside any containers 1568 | (Scored)" 1569 | audit: docker ps --quiet --all | xargs docker inspect --format '{{ .Id }}:Volumes={{ .Mounts }}' | grep docker.sock 1570 | tests: 1571 | test_items: 1572 | - flag: "docker.sock" 1573 | set: false 1574 | remediation: | 1575 | Ensure that no containers mount docker.sock as a volume. 1576 | scored: true 1577 | 1578 | - id: 6 1579 | description: "Docker Security Operations" 1580 | checks: 1581 | - id: 6.1 1582 | description: "Ensure image sprawl is avoided (Not Scored)" 1583 | type: manual 1584 | remediation: | 1585 | Keep the set of the images that you actually need and establish a workflow to remove old or 1586 | stale images from the host. Additionally, use features such as pull-by-digest to get specific 1587 | images from the registry. 1588 | Additionally, you can follow below set of steps to find out unused images on the system and 1589 | delete them. 1590 | Step 1 Make a list of all image IDs that are currently instantiated by executing below 1591 | command: 1592 | docker images --quiet | xargs docker inspect --format '{{ .Id }}: Image={{ 1593 | .Config.Image }}' 1594 | Step 2: List all the images present on the system by executing below command: 1595 | docker images 1596 | Step 3: Compare the list of image IDs populated from Step 1 and Step 2 and find out images 1597 | that are currently not being instantiated. 1598 | Step 4: Decide if you want to keep the images that are not currently in use. If not delete 1599 | them by executing below command: 1600 | docker rmi $IMAGE_ID 1601 | scored: false 1602 | 1603 | - id: 6.2 1604 | description: "Ensure container sprawl is avoided (Not Scored)" 1605 | type: manual 1606 | remediation: | 1607 | Periodically check your container inventory per host and clean up the stopped containers 1608 | using the below command: 1609 | docker container prune 1610 | scored: false 1611 | 1612 | - id: 7 1613 | description: "Docker Swarm Configuration" 1614 | constraints: 1615 | docker-swarm: 1616 | - active 1617 | checks: 1618 | - id: 7.1 1619 | description: "Ensure swarm mode is not Enabled, if not needed (Scored)" 1620 | type: manual 1621 | remediation: | 1622 | If swarm mode has been enabled on a system in error, run 1623 | docker swarm leave 1624 | scored: true 1625 | 1626 | - id: 7.2 1627 | description: "Ensure the minimum number of manager nodes have been created 1628 | in a swarm (Scored)" 1629 | type: manual 1630 | remediation: | 1631 | If an excessive number of managers is configured, the excess can be demoted as worker 1632 | using the following command: 1633 | docker node demote 1634 | Where is the node ID value of the manager to be demoted. 1635 | scored: true 1636 | 1637 | - id: 7.3 1638 | description: "Ensure swarm services are binded to a specific host interface 1639 | (Scored)" 1640 | audit: netstat -lt | grep -i 2377 1641 | tests: 1642 | test_items: 1643 | - flag: "0.0.0.0" 1644 | set: false 1645 | remediation: | 1646 | Remediation of this requires re-initialization of the swarm specifying a specific interface 1647 | for the --listen-addr parameter. 1648 | scored: true 1649 | 1650 | - id: 7.4 1651 | description: "Ensure data exchanged between containers are encrypted on 1652 | different nodes on the overlay network (Scored)" 1653 | audit: docker network ls --filter driver=overlay --quiet | xargs docker network inspect --format '{{.Name}} {{ .Options }}' 1654 | use_multiple_values: true 1655 | tests: 1656 | bin_op: or 1657 | test_items: 1658 | - flag: "" 1659 | compare: 1660 | op: eq 1661 | value: "" 1662 | set: true 1663 | - flag: "encrypted" 1664 | set: true 1665 | remediation: | 1666 | Create overlay network with --opt encrypted flag. 1667 | scored: true 1668 | 1669 | - id: 7.5 1670 | description: "Ensure Docker's secret management commands are used for 1671 | managing secrets in a Swarm cluster (Not Scored)" 1672 | type: manual 1673 | remediation: | 1674 | Follow docker secret documentation and use it to manage secrets effectively. 1675 | scored: true 1676 | 1677 | - id: 7.6 1678 | description: "Ensure swarm manager is run in auto-lock mode (Scored)" 1679 | audit: docker swarm unlock-key 1680 | tests: 1681 | test_items: 1682 | - flag: "no unlock key is set" 1683 | set: false 1684 | remediation: | 1685 | If you are initializing swarm, use the below command. 1686 | docker swarm init --autolock 1687 | If you want to set --autolock on an existing swarm manager node, use the below 1688 | command. 1689 | docker swarm update --autolock 1690 | scored: true 1691 | 1692 | - id: 7.7 1693 | description: "Ensure swarm manager auto-lock key is rotated periodically (Not 1694 | Scored)" 1695 | type: manual 1696 | remediation: | 1697 | Run the below command to rotate the keys. 1698 | docker swarm unlock-key --rotate 1699 | Additionally, to facilitate audit for this recommendation, maintain key rotation records and 1700 | ensure that you establish a pre-defined frequency for key rotation. 1701 | scored: false 1702 | 1703 | - id: 7.8 1704 | description: "Ensure node certificates are rotated as appropriate (Not Scored)" 1705 | type: manual 1706 | remediation: | 1707 | Run the below command to set the desired expiry time. 1708 | For example, 1709 | docker swarm update --cert-expiry 48h 1710 | scored: false 1711 | 1712 | - id: 7.9 1713 | description: "Ensure CA certificates are rotated as appropriate (Not Scored)" 1714 | type: manual 1715 | remediation: | 1716 | Run the below command to rotate the certificate. 1717 | docker swarm ca --rotate 1718 | scored: false 1719 | 1720 | - id: 7.10 1721 | description: "Ensure management plane traffic has been separated from data 1722 | plane traffic (Not Scored)" 1723 | type: manual 1724 | remediation: | 1725 | Initialize Swarm with dedicated interfaces for management and data planes respectively. 1726 | For example, 1727 | docker swarm init --advertise-addr=192.168.0.1 --data-path-addr=17.1.0.3 1728 | scored: false 1729 | -------------------------------------------------------------------------------- /cfg/cis-1.2/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ## Controls Files. 3 | # These are YAML files that hold all the details for running checks. 4 | # In here you can set all parameter substitution for docker-bench 5 | 6 | docker-storage: 7 | value: /var/lib/docker 8 | audit-log: 9 | value: /var/log/audit/audit.log 10 | -------------------------------------------------------------------------------- /cfg/cis-1.6.0/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ## Controls Files. 3 | # These are YAML files that hold all the details for running checks. 4 | # In here you can set all parameter substitution for docker-bench 5 | 6 | docker-storage: 7 | value: /var/lib/docker 8 | 9 | docker-config-file: 10 | value: /etc/docker/daemon.json 11 | 12 | audit-log: 13 | value: /var/log/audit/audit.log 14 | -------------------------------------------------------------------------------- /cfg/ocp-3.9/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ## Controls Files. 3 | # These are YAML files that hold all the details for running checks. 4 | # In here you can set all parameter substitution for docker-bench 5 | 6 | docker-storage: 7 | value: /var/lib/docker 8 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/aquasecurity/docker-bench 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/aquasecurity/bench-common v0.4.7-0.20230816134523-b6528bb7bd9c 7 | github.com/golang/glog v1.1.2 8 | github.com/hashicorp/go-version v1.6.0 9 | github.com/spf13/cobra v1.7.0 10 | github.com/spf13/viper v1.16.0 11 | ) 12 | 13 | require ( 14 | github.com/fatih/color v1.15.0 // indirect 15 | github.com/fsnotify/fsnotify v1.6.0 // indirect 16 | github.com/hashicorp/hcl v1.0.0 // indirect 17 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 18 | github.com/magiconair/properties v1.8.7 // indirect 19 | github.com/mattn/go-colorable v0.1.13 // indirect 20 | github.com/mattn/go-isatty v0.0.19 // indirect 21 | github.com/mitchellh/mapstructure v1.5.0 // indirect 22 | github.com/onsi/ginkgo v1.16.5 // indirect 23 | github.com/pelletier/go-toml/v2 v2.0.9 // indirect 24 | github.com/spf13/afero v1.9.5 // indirect 25 | github.com/spf13/cast v1.5.1 // indirect 26 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 27 | github.com/spf13/pflag v1.0.5 // indirect 28 | github.com/subosito/gotenv v1.6.0 // indirect 29 | golang.org/x/sys v0.11.0 // indirect 30 | golang.org/x/text v0.12.0 // indirect 31 | gopkg.in/ini.v1 v1.67.0 // indirect 32 | gopkg.in/yaml.v2 v2.4.0 // indirect 33 | gopkg.in/yaml.v3 v3.0.1 // indirect 34 | k8s.io/client-go v11.0.0+incompatible // indirect 35 | ) 36 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 41 | github.com/aquasecurity/bench-common v0.4.7-0.20230816134523-b6528bb7bd9c h1:Vr1S0LSN+EEz71uw3s3KZEcP69b8pNyOzQmybJaJv4c= 42 | github.com/aquasecurity/bench-common v0.4.7-0.20230816134523-b6528bb7bd9c/go.mod h1:cqJgzhTs9JOM2vWFRtXIbddIUOUBkwuYoGYf9BZcs8Y= 43 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 44 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 45 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 46 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 47 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 48 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 49 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 50 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 51 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 52 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 53 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 54 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 56 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 57 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 58 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 59 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 60 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 61 | github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= 62 | github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= 63 | github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= 64 | github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 65 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 66 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 67 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 68 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 69 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 70 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 71 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 72 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 73 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 74 | github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= 75 | github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= 76 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 77 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 78 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 79 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 80 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 81 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 82 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 83 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 84 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 85 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 86 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 87 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 88 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 89 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 90 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 91 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 92 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 93 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 94 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 95 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 96 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 97 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 98 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 99 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 100 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 101 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 102 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 103 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 104 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 105 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 106 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 107 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 108 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 109 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 110 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 111 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 112 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 113 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 114 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 115 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 116 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 117 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 118 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 119 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 120 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 121 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 122 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 123 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 124 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 125 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 126 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 127 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 128 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 129 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 130 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 131 | github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= 132 | github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 133 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 134 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 135 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 136 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 137 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 138 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 139 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 140 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 141 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 142 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 143 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 144 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 145 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 146 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 147 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 148 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 149 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 150 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 151 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 152 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 153 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 154 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 155 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 156 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 157 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 158 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 159 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 160 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 161 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 162 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 163 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 164 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 165 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 166 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 167 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 168 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 169 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 170 | github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= 171 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 172 | github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= 173 | github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 174 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 175 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 176 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 177 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 178 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 179 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 180 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 181 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 182 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 183 | github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= 184 | github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 185 | github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= 186 | github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= 187 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 188 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 189 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 190 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 191 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 192 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 193 | github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= 194 | github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= 195 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 196 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 197 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 198 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 199 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 200 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 201 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 202 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 203 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 204 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 205 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 206 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 207 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 208 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 209 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 210 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 211 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 212 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 213 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 214 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 215 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 216 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 217 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 218 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 219 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 220 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 221 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 222 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 223 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 224 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 225 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 226 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 227 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 228 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 229 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 230 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 231 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 232 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 233 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 234 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 235 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 236 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 237 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 238 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 239 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 240 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 241 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 242 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 243 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 244 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 245 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 246 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 247 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 248 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 249 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 250 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 251 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 252 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 253 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 254 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 255 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 256 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 257 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 258 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 259 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 260 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 261 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 262 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 263 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 264 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 265 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 266 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 267 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 268 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 269 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 270 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 271 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 272 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 273 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 274 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 275 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 276 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 277 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 278 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 279 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 280 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 281 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 282 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 283 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 284 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 285 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 286 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 287 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 288 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 289 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 290 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 291 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 292 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 293 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 294 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 295 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 296 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 297 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 298 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 299 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 300 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 301 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 302 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 303 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 304 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 305 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 306 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 307 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 308 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 309 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 310 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 311 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 312 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 313 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 314 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 315 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 316 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 317 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 318 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 319 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 320 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 321 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 322 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 323 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 324 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 325 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 326 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 327 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 328 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 329 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 330 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 331 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 332 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 333 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 334 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 335 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 336 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 337 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 338 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 339 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 340 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 341 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 342 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 343 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 344 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 345 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 346 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 347 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 348 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 349 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 350 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 351 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 352 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 353 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 354 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 355 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 356 | golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= 357 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 358 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 359 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 360 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 361 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 362 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 363 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 364 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 365 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 366 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 367 | golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= 368 | golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 369 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 370 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 371 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 372 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 373 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 374 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 375 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 376 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 377 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 378 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 379 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 380 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 381 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 382 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 383 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 384 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 385 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 386 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 387 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 388 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 389 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 390 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 391 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 392 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 393 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 394 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 395 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 396 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 397 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 398 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 399 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 400 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 401 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 402 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 403 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 404 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 405 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 406 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 407 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 408 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 409 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 410 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 411 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 412 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 413 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 414 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 415 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 416 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 417 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 418 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 419 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 420 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 421 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 422 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 423 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 424 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= 425 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 426 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 427 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 428 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 429 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 430 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 431 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 432 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 433 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 434 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 435 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 436 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 437 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 438 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 439 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 440 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 441 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 442 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 443 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 444 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 445 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 446 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 447 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 448 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 449 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 450 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 451 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 452 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 453 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 454 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 455 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 456 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 457 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 458 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 459 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 460 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 461 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 462 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 463 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 464 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 465 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 466 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 467 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 468 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 469 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 470 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 471 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 472 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 473 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 474 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 475 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 476 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 477 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 478 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 479 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 480 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 481 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 482 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 483 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 484 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 485 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 486 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 487 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 488 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 489 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 490 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 491 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 492 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 493 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 494 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 495 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 496 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 497 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 498 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 499 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 500 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 501 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 502 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 503 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 504 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 505 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 506 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 507 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 508 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 509 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 510 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 511 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 512 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 513 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 514 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 515 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 516 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 517 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 518 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 519 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 520 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 521 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 522 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 523 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 524 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 525 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 526 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 527 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 528 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 529 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 530 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 531 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 532 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 533 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 534 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 535 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 536 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 537 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 538 | k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= 539 | k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= 540 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 541 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 542 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 543 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Aqua Security Software Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | func main() { 18 | Execute() 19 | } 20 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | tests: 2 | GO111MODULE=on go test -v -short -race -timeout 30s -coverprofile=coverage.txt -covermode=atomic ./... 3 | -------------------------------------------------------------------------------- /root.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Aqua Security Software Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | 21 | goflag "flag" 22 | 23 | "github.com/spf13/cobra" 24 | "github.com/spf13/viper" 25 | ) 26 | 27 | var ( 28 | noResults bool 29 | noSummary bool 30 | noRemediations bool 31 | 32 | dockerVersion string 33 | benchmarkVersion string 34 | cfgDir string 35 | cfgFile string 36 | checkList string 37 | name string 38 | jsonFmt bool 39 | includeTestOutput bool 40 | outputFile string 41 | ) 42 | 43 | // RootCmd represents the base command when called without any subcommands 44 | var RootCmd = &cobra.Command{ 45 | Use: "docker-bench", 46 | Short: "Docker-bench is a Go application that checks whether Docker is deployed securely", 47 | Long: `This tool runs the CIS Docker Benchmark (https://www.cisecurity.org/benchmark/docker/)`, 48 | Run: app, 49 | } 50 | 51 | // Execute adds all child commands to the root command sets flags appropriately. 52 | // This is called by main.main(). It only needs to happen once to the rootCmd. 53 | func Execute() { 54 | goflag.Set("logtostderr", "true") 55 | goflag.CommandLine.Parse([]string{}) 56 | 57 | if err := RootCmd.Execute(); err != nil { 58 | fmt.Println(err) 59 | os.Exit(-1) 60 | } 61 | } 62 | 63 | func init() { 64 | cobra.OnInitialize(initConfig) 65 | 66 | // Here you will define your flags and configuration settings. 67 | // Cobra supports Persistent Flags, which, if defined here, 68 | // will be global for your application. 69 | 70 | // RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.docker-bench.yaml)") 71 | // Cobra also supports local flags, which will only run 72 | // when this action is called directly. 73 | RootCmd.PersistentFlags().BoolVar(&noResults, "noresults", false, "Disable printing of results section") 74 | RootCmd.PersistentFlags().BoolVar(&noSummary, "nosummary", false, "Disable printing of summary section") 75 | RootCmd.PersistentFlags().BoolVar(&noRemediations, "noremediations", false, "Disable printing of remediations section") 76 | RootCmd.Flags().StringVarP(&dockerVersion, "version", "", "", "Specify Docker version, automatically detected if unset") 77 | RootCmd.Flags().StringVarP(&benchmarkVersion, "benchmark", "", "", "Manually specify CIS benchmark version. It would be an error to specify both --version and --benchmark flags") 78 | RootCmd.Flags().StringVarP(&cfgDir, "config-dir", "D", "cfg", "directory to get benchmark definitions") 79 | RootCmd.PersistentFlags().BoolVar(&jsonFmt, "json", false, "Prints the results as JSON") 80 | RootCmd.PersistentFlags().BoolVar(&includeTestOutput, "include-test-output", false, "Prints the test's output") 81 | RootCmd.PersistentFlags().StringVar(&outputFile, "outputfile", "", "Writes the JSON results to output file") 82 | RootCmd.PersistentFlags().StringVarP( 83 | &checkList, 84 | "check", 85 | "c", 86 | "", 87 | `A comma-delimited list of checks to run as specified in CIS document. Example --check="1.1.1,1.1.2"`, 88 | ) 89 | 90 | goflag.CommandLine.VisitAll(func(goflag *goflag.Flag) { 91 | RootCmd.PersistentFlags().AddGoFlag(goflag) 92 | }) 93 | 94 | } 95 | 96 | // initConfig reads in config file and ENV variables if set. 97 | func initConfig() { 98 | if cfgFile != "" { // enable ability to specify config file via flag 99 | viper.SetConfigFile(cfgFile) 100 | } 101 | 102 | viper.SetConfigName(".docker-bench") // name of config file (without extension) 103 | viper.AddConfigPath("$HOME") // adding home directory as first search path 104 | viper.AutomaticEnv() // read in environment variables that match 105 | 106 | // If a config file is found, read it in. 107 | if err := viper.ReadInConfig(); err == nil { 108 | fmt.Println("Using config file:", viper.ConfigFileUsed()) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os/exec" 5 | "strings" 6 | ) 7 | 8 | func GetDockerSwarm() (platform string, err error) { 9 | res, err := exec.Command("sh", "-c", "docker info | grep Swarm").CombinedOutput() 10 | if err != nil { 11 | return "", err 12 | } 13 | 14 | if strings.Contains(string(res), "inactive") { 15 | return "inactive", nil 16 | } 17 | return "active", nil 18 | } 19 | --------------------------------------------------------------------------------