mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Add 'orbit/' from commit 'ab3047bb39f1e2be331d1ff18b4eb768619033c4'
git-subtree-dir: orbit git-subtree-mainline:d5974aad97git-subtree-split:ab3047bb39
This commit is contained in:
commit
3ac8494d23
60 changed files with 5288 additions and 0 deletions
1
orbit/.gitattributes
vendored
Normal file
1
orbit/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.go text eol=lf
|
||||
32
orbit/.github/workflows/go.yml
vendored
Normal file
32
orbit/.github/workflows/go.yml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
name: build-go
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.15
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
19
orbit/.github/workflows/golangci-lint.yml
vendored
Normal file
19
orbit/.github/workflows/golangci-lint.yml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be
|
||||
# specified without patch version: we always use the latest patch
|
||||
# version.
|
||||
version: v1.33
|
||||
24
orbit/.gitignore
vendored
Normal file
24
orbit/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
.DS_Store
|
||||
.idea
|
||||
*.log
|
||||
tmp/
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Build/packaging results
|
||||
dist/
|
||||
*.pkg
|
||||
*.deb
|
||||
*.rpm
|
||||
*.msi
|
||||
77
orbit/.goreleaser.yml
Normal file
77
orbit/.goreleaser.yml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
|
||||
builds:
|
||||
- id: orbit
|
||||
dir: ./cmd/orbit/
|
||||
binary: orbit
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
ldflags:
|
||||
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser
|
||||
hooks:
|
||||
post: ./tools/build/sign-macos.sh {{ .Path }}
|
||||
|
||||
- id: orbit-package
|
||||
dir: ./cmd/package/
|
||||
binary: orbit-package
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
ldflags:
|
||||
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser
|
||||
hooks:
|
||||
post: ./tools/build/sign-macos.sh {{ .Path }}
|
||||
|
||||
archives:
|
||||
- id: orbit
|
||||
builds:
|
||||
- orbit
|
||||
name_template: orbit_{{.Version}}_{{.Os}}
|
||||
replacements:
|
||||
darwin: macos
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
|
||||
- id: orbit-package
|
||||
builds:
|
||||
- orbit-package
|
||||
name_template: orbit-package_{{.Version}}_{{.Os}}
|
||||
replacements:
|
||||
darwin: macos
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-untagged"
|
||||
|
||||
changelog:
|
||||
skip: true
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: fleetdm
|
||||
name: orbit
|
||||
draft: true
|
||||
7
orbit/LICENSE
Normal file
7
orbit/LICENSE
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Copyright (c) 2021-present Fleet Device Management Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
203
orbit/README.md
Normal file
203
orbit/README.md
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
<img width="200" alt="Fleet logo, landscape, dark text, transparent background" src="https://user-images.githubusercontent.com/618009/103300491-9197e280-49c4-11eb-8677-6b41027be800.png">
|
||||
|
||||
# Orbit osquery
|
||||
|
||||
Orbit is an [osquery](https://github.com/osquery/osquery) runtime and autoupdater. With Orbit, it's easy to deploy osquery, manage configurations, and stay up to date. Orbit eases the deployment of osquery connected with a [Fleet server](https://github.com/fleetdm/fleet), and is a (near) drop-in replacement for osquery in a variety of deployment scenarios.
|
||||
|
||||
Orbit is the recommended agent for Fleet. But Orbit can be used with or without Fleet, and Fleet can be used with or without Orbit.
|
||||
|
||||
## Try Orbit
|
||||
|
||||
#### With [`fleetctl preview` already running](https://github.com/fleetdm/fleet#try-fleet) and [Go](https://golang.org/doc/install) 1.16 installed:
|
||||
|
||||
```bash
|
||||
# From within the top-level directory of this repo…
|
||||
# Generate a macOS installer pointed at your local Fleet
|
||||
go run ./cmd/package --type=pkg --fleet-url=localhost:8412 --insecure --enroll-secret=YOUR_FLEET_ENROLL_SECRET_HERE
|
||||
```
|
||||
|
||||
> With fleetctl preview running, you can find your Fleet enroll secret by selecting the "Add new host" button on the Hosts page in the Fleet UI.
|
||||
|
||||
An installer configured to point at your Fleet instance has now been generated.
|
||||
|
||||
Now run that installer (double click, on a Mac) to enroll your own computer as a host in Fleet. Refresh after several seconds (≈30s), and you should now see your local computer as a new host in Fleet.
|
||||
|
||||
## Bugs
|
||||
|
||||
To report a bug or request a feature, [click here](https://github.com/fleetdm/fleet/issues).
|
||||
|
||||
## Capabilities
|
||||
|
||||
| Capability | Status |
|
||||
| ------------------------------------ | ------ |
|
||||
| Secure autoupdate for osquery | ✅ |
|
||||
| Secure autoupdate for Orbit | ✅ |
|
||||
| Configurable update channels | ✅ |
|
||||
| Full osquery flag customization | ✅ |
|
||||
| Package tooling for macOS `.pkg` | ✅ |
|
||||
| Package tooling for Linux `.deb` | ✅ |
|
||||
| Package tooling for Linux `.rpm` | ✅ |
|
||||
| Package tooling for Windows `.msi` | ✅ |
|
||||
| Manage/update osquery extensions | 🔜 |
|
||||
| Manage cgroups for Linux performance | 🔜 |
|
||||
|
||||
## Usage
|
||||
|
||||
General information and flag documentation can be accessed by running `orbit --help`.
|
||||
|
||||
### Permissions
|
||||
|
||||
Orbit generally expects root permissions to be able to create and access it's working files.
|
||||
|
||||
To get root level permissions:
|
||||
|
||||
#### macOS/Linux
|
||||
|
||||
Prefix `orbit` commands with `sudo` (`sudo orbit ...`) or run in a root shell.
|
||||
|
||||
#### Windows
|
||||
|
||||
Run Powershell or cmd.exe with "Run as administrator" and start `orbit` commands from that shell.
|
||||
|
||||
### Osquery shell
|
||||
|
||||
Run an `osqueryi` shell with `orbit osqueryi` or `orbit shell`.
|
||||
|
||||
### Connect to a Fleet server
|
||||
|
||||
Use the `--fleet-url` and `--enroll-secret` flags to connect to a Fleet server.
|
||||
|
||||
For example:
|
||||
|
||||
```sh
|
||||
orbit --fleet-url=https://localhost:8080 --enroll-secret=the_secret_value
|
||||
```
|
||||
|
||||
Use `--fleet_certificate` to provide a path to a certificate bundle when necessary for osquery to verify the authenticity of the Fleet server (typically when using a Windows client or self-signed certificates):
|
||||
|
||||
```sh
|
||||
orbit --fleet-url=https://localhost:8080 --enroll-secret=the_secret_value --fleet-certificate=cert.pem
|
||||
```
|
||||
|
||||
Add the `--insecure` flag for connections using otherwise invalid certificates:
|
||||
|
||||
```sh
|
||||
orbit --fleet-url=https://localhost:8080 --enroll-secret=the_secret_value --insecure
|
||||
```
|
||||
|
||||
### Osquery flags
|
||||
|
||||
Orbit can be used as near drop-in replacement for `osqueryd`, enhancing standard osquery with autoupdate capabilities. Orbit passes through any options after `--` directly to the `osqueryd` instance.
|
||||
|
||||
For example, the following would be a typical drop-in usage of Orbit:
|
||||
|
||||
```sh
|
||||
orbit -- --flagfile=flags.txt
|
||||
```
|
||||
|
||||
## Packaging
|
||||
|
||||
Orbit, like standalone osquery, is typically deployed via OS-specific packages. Tooling is provided with this repository to generate installation packages.
|
||||
|
||||
### Dependencies
|
||||
|
||||
Orbit currently supports building packages on macOS and Linux.
|
||||
|
||||
Before building packages, clone or download this repository and [install Go](https://golang.org/doc/install).
|
||||
|
||||
Building Windows packages requires Docker to be installed.
|
||||
|
||||
### Packaging support
|
||||
|
||||
- **macOS** - `.pkg` package generation with (optional) [Notarization](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution) and codesigning - Persistence via `launchd`.
|
||||
|
||||
- **Linux** - `.deb` (Debian, Ubuntu, etc.) & `.rpm` (RHEL, CentOS, etc.) package generation - Persistence via `systemd`.
|
||||
|
||||
- **Windows** - `.msi` package generation - Persistence via Services.
|
||||
|
||||
### Building packages
|
||||
|
||||
Use `go run ./cmd/package` from this directory to run the packaging tools.
|
||||
|
||||
The only required parameter is `--type`, use one of `deb`, `rpm`, `pkg`, or `msi`.
|
||||
|
||||
Configure osquery to connect to a Fleet (or other TLS) server with the `--fleet-url` and `--enroll-secret` flags.
|
||||
|
||||
A minimal invocation for communicating with Fleet:
|
||||
|
||||
```sh
|
||||
go run ./cmd/package --type deb --fleet-url=fleet.example.com --enroll-secret=notsosecret
|
||||
```
|
||||
|
||||
This will build a `.deb` package configured to communicate with a Fleet server at `fleet.example.com` using the enroll secret `notsosecret`.
|
||||
|
||||
When the Fleet server uses a self-signed (or otherwise invalid) TLS certificate, package with the `--insecure` or `--fleet-certificate` options.
|
||||
|
||||
See `go run ./cmd/package` for the full range of packaging options.
|
||||
|
||||
#### Update channels
|
||||
|
||||
Orbit uses the concept of "update channels" to determine the version of Orbit, osquery, and any extensions (extension support coming soon) to run. This concept is modeled from the common versioning convention for Docker containers.
|
||||
|
||||
Configure update channels for Orbit and osqueryd with the `--orbit-channel` and `--osqueryd-channel` flags when packaging.
|
||||
|
||||
| Channel | Versions |
|
||||
| ------- | -------- |
|
||||
| `4` | 4.x.x |
|
||||
| `4.6` | 4.6.x |
|
||||
| `4.6.0` | 4.6.0 |
|
||||
|
||||
Additionally `stable` and `edge` are special channel names. `stable` will always return the version Fleet deems to be stable, while `edge` will provide newer releases for beta testing.
|
||||
|
||||
#### macOS signing & Notarization
|
||||
|
||||
Orbit's packager can automate the codesigning and Notarization steps to allow the resulting package to generate packages that appear "trusted" when install on macOS hosts. Signing & notarization are supported only on macOS hosts.
|
||||
|
||||
For signing, a "Developer ID Installer" certificate must be available on the build machine ([generation instructions](https://help.apple.com/xcode/mac/current/#/dev154b28f09)). Use `security find-identity -v` to verify the existence of this certificate and make note of the identifier provided in the left column.
|
||||
|
||||
For Notarization, valid App Store Connect credentials must be available on the build machine. Set these in the environment variables `AC_USERNAME` and `AC_PASSWORD`. It is common to configure this via [app-specific passwords](https://support.apple.com/en-ca/HT204397).
|
||||
|
||||
Build a signed and notarized macOS package with an invocation like the following:
|
||||
|
||||
```sh
|
||||
AC_USERNAME=zach@example.com AC_PASSWORD=llpk-sije-kjlz-jdzw go run ./cmd/package --type=pkg --fleet-url=fleet.example.com --enroll-secret=63SBzTT+2UyW --sign-identity 3D7260BF99539C6E80A94835A8921A988F4E6498 --notarize
|
||||
```
|
||||
|
||||
This process may take several minutes to complete as the Notarization process completes on Apple's servers.
|
||||
|
||||
After successful notarization, the generated "ticket" is automatically stapled to the package.
|
||||
|
||||
## FAQs
|
||||
|
||||
### How does Orbit compare with Kolide Launcher?
|
||||
|
||||
Orbit is inspired by the success of [Kolide Launcher](https://github.com/kolide/launcher), and approaches a similar problem domain with new strategies informed by the challenges encountered in real world deployments. Orbit does not share any code with Launcher.
|
||||
|
||||
- Both Orbit and Launcher use [The Update Framework](https://theupdateframework.com/) specification for managing updates. Orbit utilizes the official [go-tuf](https://github.com/theupdateframework/go-tuf) library, while Launcher has it's own implementation of the specification.
|
||||
- Orbit can be deployed as a (near) drop-in replacement for osquery, supporting full customization of the osquery flags. Launcher heavily manages the osquery flags making deployment outside of Fleet or Kolide's SaaS difficult.
|
||||
- Orbit prefers the battle-tested plugins of osquery. Orbit uses the built-in logging, configuration, and live query plugins, while Launcher uses custom implementations.
|
||||
- Orbit prefers the built-in osquery remote APIs. Launcher utilizes a custom gRPC API that has led to issues with character encoding, load balancers/proxies, and request size limits.
|
||||
- Orbit encourages use of the osquery performance Watchdog, while Launcher disables the Watchdog.
|
||||
|
||||
Additionally, Orbit aims to tackle problems out of scope for Launcher:
|
||||
|
||||
- Configure updates via release channels, providing more granular control over agent versioning.
|
||||
- Support for deploying and updating osquery extensions (🔜).
|
||||
- Manage osquery versions and startup flags from a remote (Fleet) server (🔜).
|
||||
- Further control of osquery performance via cgroups (🔜).
|
||||
|
||||
### Is Orbit Free?
|
||||
|
||||
Yes! Orbit is licensed under an MIT license and all uses are encouraged.
|
||||
|
||||
### How does orbit update osquery? And how do the stable and edge channels get triggered to update osquery on a self hosted Fleet instance?
|
||||
|
||||
Orbit uses a configurable update server. We expect that many folks will just use the update server we manage (similar to what Kolide does with Launcher's update server). We are also offering [tooling for self-managing an update server](https://github.com/fleetdm/fleet/blob/master/docs/3-Deployment/4-fleetctl-agent-updates.md) as part of Fleet Basic (the subscription offering).
|
||||
|
||||
## Community
|
||||
|
||||
#### Chat
|
||||
|
||||
Please join us in the #fleet channel on [osquery Slack](https://osquery.slack.com/join/shared_invite/zt-h29zm0gk-s2DBtGUTW4CFel0f0IjTEw#/).
|
||||
|
||||
<a href="https://fleetdm.com"><img alt="Banner featuring a futuristic cloud city with the Fleet logo" src="https://user-images.githubusercontent.com/618009/98254443-eaf21100-1f41-11eb-9e2c-63a0545601f3.jpg"/></a>
|
||||
1
orbit/changes/.keep
Normal file
1
orbit/changes/.keep
Normal file
|
|
@ -0,0 +1 @@
|
|||
IGNORE BUT DO NOT REMOVE. THIS IS HERE SO THAT THE DIRECTORY REMAINS
|
||||
1
orbit/changes/build-msi-root
Normal file
1
orbit/changes/build-msi-root
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Fix permissions for building MSI when packaging as root user. Fixes fleetdm/fleet#1424
|
||||
377
orbit/cmd/orbit/orbit.go
Normal file
377
orbit/cmd/orbit/orbit.go
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
"github.com/fleetdm/orbit/pkg/certificate"
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/database"
|
||||
"github.com/fleetdm/orbit/pkg/insecure"
|
||||
"github.com/fleetdm/orbit/pkg/osquery"
|
||||
"github.com/fleetdm/orbit/pkg/update"
|
||||
"github.com/fleetdm/orbit/pkg/update/filestore"
|
||||
"github.com/oklog/run"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
// Flags set by goreleaser during build
|
||||
version = ""
|
||||
commit = ""
|
||||
date = ""
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "Orbit osquery"
|
||||
app.Usage = "A powered-up, (near) drop-in replacement for osquery"
|
||||
app.Commands = []*cli.Command{
|
||||
versionCommand,
|
||||
shellCommand,
|
||||
}
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "root-dir",
|
||||
Usage: "Root directory for Orbit state",
|
||||
Value: update.DefaultOptions.RootDirectory,
|
||||
EnvVars: []string{"ORBIT_ROOT_DIR"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "insecure",
|
||||
Usage: "Disable TLS certificate verification",
|
||||
EnvVars: []string{"ORBIT_INSECURE"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "fleet-url",
|
||||
Usage: "URL (host:port) of Fleet server",
|
||||
EnvVars: []string{"ORBIT_FLEET_URL"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "fleet-certificate",
|
||||
Usage: "Path to server cerificate bundle",
|
||||
EnvVars: []string{"ORBIT_FLEET_CERTIFICATE"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "update-url",
|
||||
Usage: "URL for update server",
|
||||
Value: "https://tuf.fleetctl.com",
|
||||
EnvVars: []string{"ORBIT_UPDATE_URL"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enroll-secret",
|
||||
Usage: "Enroll secret for authenticating to Fleet server",
|
||||
EnvVars: []string{"ORBIT_ENROLL_SECRET"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enroll-secret-path",
|
||||
Usage: "Path to file containing enroll secret",
|
||||
EnvVars: []string{"ORBIT_ENROLL_SECRET_PATH"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "osqueryd-channel",
|
||||
Usage: "Update channel of osqueryd to use",
|
||||
Value: "stable",
|
||||
EnvVars: []string{"ORBIT_OSQUERYD_CHANNEL"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "orbit-channel",
|
||||
Usage: "Update channel of Orbit to use",
|
||||
Value: "stable",
|
||||
EnvVars: []string{"ORBIT_ORBIT_CHANNEL"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "Enable debug logging",
|
||||
EnvVars: []string{"ORBIT_DEBUG"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "version",
|
||||
Usage: "Get Orbit version",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "log-file",
|
||||
Usage: "Log to this file path in addition to stderr",
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) error {
|
||||
if c.Bool("version") {
|
||||
fmt.Println("orbit " + version)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true})
|
||||
if logfile := c.String("log-file"); logfile != "" {
|
||||
f, err := os.OpenFile(logfile, os.O_CREATE|os.O_APPEND, 0o600)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open logfile")
|
||||
}
|
||||
log.Logger = log.Output(zerolog.MultiLevelWriter(
|
||||
zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true},
|
||||
zerolog.ConsoleWriter{Out: f, TimeFormat: time.RFC3339Nano, NoColor: true},
|
||||
))
|
||||
}
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
|
||||
if c.Bool("debug") {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
|
||||
if c.Bool("insecure") && c.String("fleet-certificate") != "" {
|
||||
return errors.New("insecure and fleet-certificate may not be specified together")
|
||||
}
|
||||
|
||||
if c.String("enroll-secret-path") != "" {
|
||||
if c.String("enroll-secret") != "" {
|
||||
return errors.New("enroll-secret and enroll-secret-path may not be specified together")
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(c.String("enroll-secret-path"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read enroll secret file")
|
||||
}
|
||||
|
||||
if err := c.Set("enroll-secret", strings.TrimSpace(string(b))); err != nil {
|
||||
return errors.Wrap(err, "set enroll secret from file")
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(c.String("root-dir"), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "initialize root dir")
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(c.String("root-dir"), "orbit.db")
|
||||
db, err := database.Open(dbPath)
|
||||
if err != nil {
|
||||
if errors.Is(err, badger.ErrTruncateNeeded) {
|
||||
db, err = database.OpenTruncate(dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Warn().Msg("Open badger required truncate. Data loss is possible.")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if err := db.Close(); err != nil {
|
||||
log.Error().Err(err).Msg("Close badger")
|
||||
}
|
||||
}()
|
||||
|
||||
localStore, err := filestore.New(filepath.Join(c.String("root-dir"), "tuf-metadata.json"))
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to create local metadata store")
|
||||
}
|
||||
|
||||
// Initialize updater and get expected version
|
||||
opt := update.DefaultOptions
|
||||
opt.RootDirectory = c.String("root-dir")
|
||||
opt.ServerURL = c.String("update-url")
|
||||
opt.LocalStore = localStore
|
||||
opt.InsecureTransport = c.Bool("insecure")
|
||||
updater, err := update.New(opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updater.UpdateMetadata(); err != nil {
|
||||
log.Info().Err(err).Msg("failed to update metadata. using saved metadata.")
|
||||
}
|
||||
osquerydPath, err := updater.Get("osqueryd", c.String("osqueryd-channel"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Clear leftover files from updates
|
||||
if err := filepath.Walk(c.String("root-dir"), func(path string, info fs.FileInfo, err error) error {
|
||||
// Ignore anything not containing .old extension
|
||||
if !strings.HasSuffix(path, ".old") {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
log.Info().Err(err).Msg("failed to remove .old")
|
||||
return nil
|
||||
}
|
||||
log.Debug().Str("path", path).Msg("cleaned up old")
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "cleanup old files")
|
||||
}
|
||||
|
||||
var g run.Group
|
||||
|
||||
updateRunner, err := update.NewRunner(updater, update.RunnerOptions{
|
||||
CheckInterval: 10 * time.Second,
|
||||
Targets: map[string]string{
|
||||
"osqueryd": c.String("osqueryd-channel"),
|
||||
"orbit": c.String("orbit-channel"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Add(updateRunner.Execute, updateRunner.Interrupt)
|
||||
|
||||
var options []func(*osquery.Runner) error
|
||||
options = append(options, osquery.WithDataPath(c.String("root-dir")))
|
||||
|
||||
fleetURL := c.String("fleet-url")
|
||||
if !strings.HasPrefix(fleetURL, "http") {
|
||||
fleetURL = "https://" + fleetURL
|
||||
}
|
||||
|
||||
enrollSecret := c.String("enroll-secret")
|
||||
if enrollSecret != "" {
|
||||
options = append(options,
|
||||
osquery.WithEnv([]string{"ENROLL_SECRET=" + enrollSecret}),
|
||||
osquery.WithFlags([]string{"--enroll_secret_env=ENROLL_SECRET"}),
|
||||
)
|
||||
}
|
||||
|
||||
if fleetURL != "https://" && c.Bool("insecure") {
|
||||
proxy, err := insecure.NewTLSProxy(fleetURL)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create TLS proxy")
|
||||
}
|
||||
|
||||
g.Add(
|
||||
func() error {
|
||||
log.Info().
|
||||
Str("addr", fmt.Sprintf("localhost:%d", proxy.Port)).
|
||||
Str("target", c.String("fleet-url")).
|
||||
Msg("using insecure TLS proxy")
|
||||
err := proxy.InsecureServeTLS()
|
||||
return err
|
||||
},
|
||||
func(error) {
|
||||
if err := proxy.Close(); err != nil {
|
||||
log.Error().Err(err).Msg("close proxy")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
certPath := filepath.Join(os.TempDir(), "fleet.crt")
|
||||
|
||||
// Write cert that proxy uses
|
||||
err = ioutil.WriteFile(certPath, []byte(insecure.ServerCert), os.ModePerm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "write server cert")
|
||||
}
|
||||
|
||||
// Rewrite URL to the proxy URL. Note the proxy handles any URL
|
||||
// prefix so we don't need to carry that over here.
|
||||
parsedURL := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: fmt.Sprintf("localhost:%d", proxy.Port),
|
||||
}
|
||||
|
||||
// Check and log if there are any errors with TLS connection.
|
||||
pool, err := certificate.LoadPEM(certPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "load certificate")
|
||||
}
|
||||
if err := certificate.ValidateConnection(pool, fleetURL); err != nil {
|
||||
log.Info().Err(err).Msg("Failed to connect to Fleet server. Osquery connection may fail.")
|
||||
}
|
||||
|
||||
options = append(options,
|
||||
osquery.WithFlags(osquery.FleetFlags(parsedURL)),
|
||||
osquery.WithFlags([]string{"--tls_server_certs", certPath}),
|
||||
)
|
||||
} else if fleetURL != "https://" {
|
||||
if enrollSecret == "" {
|
||||
return errors.New("enroll secret must be specified to connect to Fleet server")
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(fleetURL)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "parse URL")
|
||||
}
|
||||
|
||||
options = append(options,
|
||||
osquery.WithFlags(osquery.FleetFlags(parsedURL)),
|
||||
)
|
||||
|
||||
if certPath := c.String("fleet-certificate"); certPath != "" {
|
||||
// Check and log if there are any errors with TLS connection.
|
||||
pool, err := certificate.LoadPEM(certPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "load certificate")
|
||||
}
|
||||
if err := certificate.ValidateConnection(pool, fleetURL); err != nil {
|
||||
log.Info().Err(err).Msg("Failed to connect to Fleet server. Osquery connection may fail.")
|
||||
}
|
||||
|
||||
options = append(options,
|
||||
osquery.WithFlags([]string{"--tls_server_certs=" + certPath}),
|
||||
)
|
||||
} else {
|
||||
// Check and log if there are any errors with TLS connection.
|
||||
pool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
log.Info().Err(err).Msg("Failed to retrieve system cert pool. Cannot validate Fleet server connection.")
|
||||
} else if err := certificate.ValidateConnection(pool, fleetURL); err != nil {
|
||||
log.Info().Err(err).Msg("Failed to connect to Fleet server. Osquery connection may fail. Provide certificate with --fleet-certificate.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --force is sometimes needed when an older osquery process has not
|
||||
// exited properly
|
||||
options = append(options, osquery.WithFlags([]string{"--force"}))
|
||||
|
||||
if c.Bool("debug") {
|
||||
options = append(options,
|
||||
osquery.WithFlags([]string{"--verbose", "--tls_dump"}),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle additional args after --
|
||||
options = append(options, osquery.WithFlags(c.Args().Slice()))
|
||||
|
||||
// Create an osquery runner with the provided options
|
||||
r, _ := osquery.NewRunner(osquerydPath, options...)
|
||||
g.Add(r.Execute, r.Interrupt)
|
||||
|
||||
// Install a signal handler
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
g.Add(run.SignalHandler(ctx, os.Interrupt, os.Kill))
|
||||
|
||||
if err := g.Run(); err != nil {
|
||||
log.Error().Err(err).Msg("unexpected exit")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Error().Err(err).Msg("")
|
||||
}
|
||||
}
|
||||
|
||||
var versionCommand = &cli.Command{
|
||||
Name: "version",
|
||||
Usage: "Get the orbit version",
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("orbit " + version)
|
||||
fmt.Println("commit - " + commit)
|
||||
fmt.Println("date - " + date)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
90
orbit/cmd/orbit/shell.go
Normal file
90
orbit/cmd/orbit/shell.go
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/osquery"
|
||||
"github.com/fleetdm/orbit/pkg/update"
|
||||
"github.com/fleetdm/orbit/pkg/update/filestore"
|
||||
"github.com/oklog/run"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var shellCommand = &cli.Command{
|
||||
Name: "shell",
|
||||
Aliases: []string{"osqueryi"},
|
||||
Usage: "Run the osqueryi shell",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "osqueryd-channel",
|
||||
Usage: "Channel of osqueryd version to use",
|
||||
Value: "stable",
|
||||
EnvVars: []string{"ORBIT_OSQUERYD_CHANNEL"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "Enable debug logging",
|
||||
EnvVars: []string{"ORBIT_DEBUG"},
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Bool("debug") {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(c.String("root-dir"), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "initialize root dir")
|
||||
}
|
||||
|
||||
localStore, err := filestore.New(filepath.Join(c.String("root-dir"), "tuf-metadata.json"))
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to create local metadata store")
|
||||
}
|
||||
|
||||
// Initialize updater and get expected version
|
||||
opt := update.DefaultOptions
|
||||
opt.RootDirectory = c.String("root-dir")
|
||||
opt.ServerURL = c.String("update-url")
|
||||
opt.LocalStore = localStore
|
||||
opt.InsecureTransport = c.Bool("insecure")
|
||||
updater, err := update.New(opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updater.UpdateMetadata(); err != nil {
|
||||
log.Info().Err(err).Msg("failed to update metadata. using saved metadata.")
|
||||
}
|
||||
osquerydPath, err := updater.Get("osqueryd", c.String("osqueryd-channel"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var g run.Group
|
||||
|
||||
// Create an osquery runner with the provided options
|
||||
r, _ := osquery.NewRunner(
|
||||
osquerydPath,
|
||||
osquery.WithShell(),
|
||||
// Handle additional args after --
|
||||
osquery.WithFlags(c.Args().Slice()),
|
||||
)
|
||||
g.Add(r.Execute, r.Interrupt)
|
||||
|
||||
// Install a signal handler
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
g.Add(run.SignalHandler(ctx, os.Interrupt, os.Kill))
|
||||
|
||||
if err := g.Run(); err != nil {
|
||||
log.Error().Err(err).Msg("unexpected exit")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
142
orbit/cmd/package/package.go
Normal file
142
orbit/cmd/package/package.go
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/packaging"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var opt packaging.Options
|
||||
log.Logger = log.Output(
|
||||
zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano},
|
||||
)
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "Orbit osquery"
|
||||
app.Usage = "A powered-up, (near) drop-in replacement for osquery"
|
||||
app.Commands = []*cli.Command{}
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "type",
|
||||
Usage: "Type of package to build",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enroll-secret",
|
||||
Usage: "Enroll secret for authenticating to Fleet server",
|
||||
Destination: &opt.EnrollSecret,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "fleet-url",
|
||||
Usage: "URL (host:port) of Fleet server",
|
||||
Destination: &opt.FleetURL,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "fleet-certificate",
|
||||
Usage: "Path to server cerificate bundle",
|
||||
Destination: &opt.FleetCertificate,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "identifier",
|
||||
Usage: "Identifier for package product",
|
||||
Value: "com.fleetdm.orbit",
|
||||
Destination: &opt.Identifier,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "Version for package product",
|
||||
Value: "0.0.3",
|
||||
Destination: &opt.Version,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "insecure",
|
||||
Usage: "Disable TLS certificate verification",
|
||||
Destination: &opt.Insecure,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "service",
|
||||
Usage: "Install orbit/osquery with a persistence service (launchd, systemd, etc.)",
|
||||
Value: true,
|
||||
Destination: &opt.StartService,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "sign-identity",
|
||||
Usage: "Identity to use for macOS codesigning",
|
||||
Destination: &opt.SignIdentity,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "notarize",
|
||||
Usage: "Whether to notarize macOS packages",
|
||||
Destination: &opt.Notarize,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "osqueryd-channel",
|
||||
Usage: "Update channel of osqueryd to use",
|
||||
Value: "stable",
|
||||
Destination: &opt.OsquerydChannel,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "orbit-channel",
|
||||
Usage: "Update channel of Orbit to use",
|
||||
Value: "stable",
|
||||
Destination: &opt.OrbitChannel,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "update-url",
|
||||
Usage: "URL for update server",
|
||||
Value: "https://tuf.fleetctl.com",
|
||||
Destination: &opt.UpdateURL,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "update-roots",
|
||||
Usage: "Root key JSON metadata for update server (from fleetctl updates roots)",
|
||||
Destination: &opt.UpdateRoots,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "Enable debug logging",
|
||||
Destination: &opt.Debug,
|
||||
},
|
||||
}
|
||||
app.Before = func(c *cli.Context) error {
|
||||
if opt.Debug {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
app.Action = func(c *cli.Context) error {
|
||||
if opt.FleetURL != "" || opt.EnrollSecret != "" {
|
||||
if opt.FleetURL == "" || opt.EnrollSecret == "" {
|
||||
return errors.New("--enroll-secret and --fleet-url must be provided together")
|
||||
}
|
||||
}
|
||||
|
||||
if opt.Insecure && opt.FleetCertificate != "" {
|
||||
return errors.New("--insecure and --fleet-certificate may not be provided together")
|
||||
}
|
||||
|
||||
switch c.String("type") {
|
||||
case "pkg":
|
||||
return packaging.BuildPkg(opt)
|
||||
case "deb":
|
||||
return packaging.BuildDeb(opt)
|
||||
case "rpm":
|
||||
return packaging.BuildRPM(opt)
|
||||
case "msi":
|
||||
return packaging.BuildMSI(opt)
|
||||
default:
|
||||
return errors.New("type must be one of ('pkg', 'deb', 'rpm', 'msi')")
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal().Err(err).Msg("package failed")
|
||||
}
|
||||
}
|
||||
19
orbit/go.mod
Normal file
19
orbit/go.mod
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
module github.com/fleetdm/orbit
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/goreleaser/nfpm/v2 v2.2.2
|
||||
github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
|
||||
github.com/mitchellh/gon v0.2.3
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/zerolog v1.20.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/theupdateframework/go-tuf v0.0.0-20201230183259-aee6270feb55
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57
|
||||
)
|
||||
852
orbit/go.sum
Normal file
852
orbit/go.sum
Normal file
|
|
@ -0,0 +1,852 @@
|
|||
4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a h1:wFEQiK85fRsEVF0CRrPAos5LoAryUsIX1kPW/WrIqFw=
|
||||
4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/Djarvur/go-err113 v0.1.0 h1:uCRZZOdMQ0TZPHYTdYpoC0bLYJKPEHPUJ8MeAa51lNU=
|
||||
github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/ashanbrown/forbidigo v1.0.0 h1:QdNXBduDUopc3GW+YVYZn8jzmIMklQiCfdN2N5+dQeE=
|
||||
github.com/ashanbrown/forbidigo v1.0.0/go.mod h1:PH+zMRWE15yW69fYfe7Kn8nYR6yYyafc3ntEGh2BBAg=
|
||||
github.com/ashanbrown/makezero v0.0.0-20201205152432-7b7cdbb3025a h1:/U9tbJzDRof4fOR51vwzWdIBsIH6R2yU0KG1MBRM2Js=
|
||||
github.com/ashanbrown/makezero v0.0.0-20201205152432-7b7cdbb3025a/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/bombsimon/wsl/v3 v3.1.0 h1:E5SRssoBgtVFPcYWUOFJEcgaySgdtTNYzsSKDOY7ss8=
|
||||
github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
|
||||
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
|
||||
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/daixiang0/gci v0.2.7 h1:bosLNficubzJZICsVzxuyNc6oAbdz0zcqLG2G/RxtY4=
|
||||
github.com/daixiang0/gci v0.2.7/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7qg9dX7pc218=
|
||||
github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-critic/go-critic v0.5.2 h1:3RJdgf6u4NZUumoP8nzbqiiNT8e1tC2Oc7jlgqre/IA=
|
||||
github.com/go-critic/go-critic v0.5.2/go.mod h1:cc0+HvdE3lFpqLecgqMaJcvWWH77sLdBp+wLGPM1Yyo=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
|
||||
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
|
||||
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
||||
github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
|
||||
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
|
||||
github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
|
||||
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
||||
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
|
||||
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
|
||||
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
|
||||
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
|
||||
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
|
||||
github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg=
|
||||
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
|
||||
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
|
||||
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
|
||||
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
||||
github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk=
|
||||
github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
|
||||
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w=
|
||||
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
|
||||
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
|
||||
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
|
||||
github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d h1:pXTK/gkVNs7Zyy7WKgLXmpQ5bHTrq5GDsp8R9Qs67g0=
|
||||
github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
|
||||
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks=
|
||||
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
|
||||
github.com/golangci/golangci-lint v1.34.1 h1:xf1yVlLBNeCIoOHWXhwqnUeaqzONllRSgiLSahNt0Mw=
|
||||
github.com/golangci/golangci-lint v1.34.1/go.mod h1:6Bnn7T0JYin7uukitgL6f9E9auQatlT0RMNOKG9lSHU=
|
||||
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI=
|
||||
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
|
||||
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
|
||||
github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo=
|
||||
github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
|
||||
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us=
|
||||
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
|
||||
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
|
||||
github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 h1:XQKc8IYQOeRwVs36tDrEmTgDgP88d5iEURwpmtiAlOM=
|
||||
github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
|
||||
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
|
||||
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/rpmpack v0.0.0-20201225075926-0a97c2c4b688 h1:jyGRCFMyDK/gKe6QRZQWci5+wEUBFElvxLHs3iwO3hY=
|
||||
github.com/google/rpmpack v0.0.0-20201225075926-0a97c2c4b688/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gookit/color v1.3.1/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/goreleaser/chglog v0.1.2 h1:tdzAb/ILeMnphzI9zQ7Nkq+T8R9qyXli8GydD8plFRY=
|
||||
github.com/goreleaser/chglog v0.1.2/go.mod h1:tTZsFuSZK4epDXfjMkxzcGbrIOXprf0JFp47BjIr3B8=
|
||||
github.com/goreleaser/fileglob v0.3.1 h1:OTFDWqUUHjQazk2N5GdUqEbqT/grBnRARaAXsV07q1Y=
|
||||
github.com/goreleaser/fileglob v0.3.1/go.mod h1:kNcPrPzjCp+Ox3jmXLU5QEsjhqrtLBm6OnXAif8KRl8=
|
||||
github.com/goreleaser/nfpm/v2 v2.2.2 h1:aLGEqhMDcSNg1RHwzUBHwRUbmiK8HfD1sTq8bG7/WMc=
|
||||
github.com/goreleaser/nfpm/v2 v2.2.2/go.mod h1:WtPzYQh+Ls57prS9RzXegrGejqILUS1aZibXHTTJ8eI=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw=
|
||||
github.com/gostaticanalysis/analysisutil v0.4.1/go.mod h1:18U/DLpRgIUd459wGxVHE0fRgmo1UgHDcbw7F5idXu0=
|
||||
github.com/gostaticanalysis/analysisutil v0.6.1 h1:/1JkoHe4DVxur+0wPvi26FoQfe1E3ZGqIXS3aaSLiaw=
|
||||
github.com/gostaticanalysis/analysisutil v0.6.1/go.mod h1:18U/DLpRgIUd459wGxVHE0fRgmo1UgHDcbw7F5idXu0=
|
||||
github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI=
|
||||
github.com/gostaticanalysis/comment v1.4.1 h1:xHopR5L2lRz6OsjH4R2HG5wRhW9ySl3FsHIvi5pcXwc=
|
||||
github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.9.3-0.20191025211905-234833755cb2 h1:STV8OvzphW1vlhPFxcG8d6OIilzBSKRAoWFJt+Onu10=
|
||||
github.com/hashicorp/go-hclog v0.9.3-0.20191025211905-234833755cb2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.3 h1:tuulM+WnToeqa05z83YLmKabZxrySOmJAd4mJ+s2Nfg=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.3/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ=
|
||||
github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jgautheron/goconst v0.0.0-20201117150253-ccae5bf973f3 h1:7nkB9fLPMwtn/R6qfPcHileL/x9ydlhw8XyDrLI1ZXg=
|
||||
github.com/jgautheron/goconst v0.0.0-20201117150253-ccae5bf973f3/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
||||
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
|
||||
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kulti/thelper v0.1.0 h1:ig1EW6yhDiRNN3dplbhdsW2gTvbxTz9i4q5Rr/tRfpk=
|
||||
github.com/kulti/thelper v0.1.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U=
|
||||
github.com/kunwardeep/paralleltest v1.0.2 h1:/jJRv0TiqPoEy/Y8dQxCFJhD56uS/pnvtatgTZBHokU=
|
||||
github.com/kunwardeep/paralleltest v1.0.2/go.mod h1:ZPqNm1fVHPllh5LPVujzbVz1JN2GhLxSfY+oqUsvG30=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77LoN/M=
|
||||
github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ=
|
||||
github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU=
|
||||
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/matoous/godox v0.0.0-20200801072554-4fb83dc2941e h1:2U5rOmpaB96l35w+NDjMtmmrp2e6a6AJKoc4B5+7UwA=
|
||||
github.com/matoous/godox v0.0.0-20200801072554-4fb83dc2941e/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mbilski/exhaustivestruct v1.1.0 h1:4ykwscnAFeHJruT+EY3M3vdeP8uXMh0VV2E61iR7XD8=
|
||||
github.com/mbilski/exhaustivestruct v1.1.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gon v0.2.3 h1:fObN7hD14VacGG++t27GzTW6opP0lwI7TsgTPL55wBo=
|
||||
github.com/mitchellh/gon v0.2.3/go.mod h1:Ua18ZhqjZHg8VyqZo8kNHAY331ntV6nNJ9mT3s2mIo8=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
||||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
|
||||
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4=
|
||||
github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k=
|
||||
github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nakabonne/nestif v0.3.0 h1:+yOViDGhg8ygGrmII72nV9B/zGxY188TYpfolntsaPw=
|
||||
github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nishanths/exhaustive v0.1.0 h1:kVlMw8h2LHPMGUVqUj6230oQjjTMFjwcZrnkhXzFfl8=
|
||||
github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9ajLT0Uf2MYZQQ=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
|
||||
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
|
||||
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA=
|
||||
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/polyfloyd/go-errorlint v0.0.0-20201127212506-19bd8db6546f h1:xAw10KgJqG5NJDfmRqJ05Z0IFblKumjtMeyiOLxj3+4=
|
||||
github.com/polyfloyd/go-errorlint v0.0.0-20201127212506-19bd8db6546f/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
github.com/quasilyte/go-ruleguard v0.2.0/go.mod h1:2RT/tf0Ce0UDj5y243iWKosQogJd8+1G3Rs2fxmlYnw=
|
||||
github.com/quasilyte/go-ruleguard v0.2.1 h1:56eRm0daAyny9UhJnmtJW/UyLZQusukBAB8oT8AHKHo=
|
||||
github.com/quasilyte/go-ruleguard v0.2.1/go.mod h1:hN2rVc/uS4bQhQKTio2XaSJSafJwqBUWWwtssT3cQmc=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200805063351-8f842688393c h1:+gtJ/Pwj2dgUGlZgTrNFqajGYKZQc7Piqus/S6DK9CE=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200805063351-8f842688393c/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryancurrah/gomodguard v1.2.0 h1:YWfhGOrXwLGiqcC/u5EqG6YeS8nh+1fw0HEc85CVZro=
|
||||
github.com/ryancurrah/gomodguard v1.2.0/go.mod h1:rNqbC4TOIdUDcVMSIpNNAzTbzXAZa6W5lnUepvuMMgQ=
|
||||
github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw=
|
||||
github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b h1:+gCnWOZV8Z/8jehJ2CdqB47Z3S+SREmQcuXkRFLNsiI=
|
||||
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sebdah/goldie v1.0.0 h1:9GNhIat69MSlz/ndaBg48vl9dF5fI+NBB6kfOxgfkMc=
|
||||
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
|
||||
github.com/securego/gosec/v2 v2.5.0 h1:kjfXLeKdk98gBe2+eYRFMpC4+mxmQQtbidpiiOQ69Qc=
|
||||
github.com/securego/gosec/v2 v2.5.0/go.mod h1:L/CDXVntIff5ypVHIkqPXbtRpJiNCh6c6Amn68jXDjo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
|
||||
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
|
||||
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY=
|
||||
github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI=
|
||||
github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ=
|
||||
github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
|
||||
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/ssgreg/nlreturn/v2 v2.1.0 h1:6/s4Rc49L6Uo6RLjhWZGBpWWjfzk2yrf1nIW8m4wgVA=
|
||||
github.com/ssgreg/nlreturn/v2 v2.1.0/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
|
||||
github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b h1:HxLVTlqcHhFAz3nWUcuvpH7WuOMv8LQoCWmruLfFH2U=
|
||||
github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
|
||||
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 h1:iGnD/q9160NWqKZZ5vY4p0dMiYMRknzctfSkqA4nBDw=
|
||||
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613/go.mod h1:g6AnIpDSYMcphz193otpSIzN+11Rs+AAIIC6rm1enug=
|
||||
github.com/tetafro/godot v1.3.2 h1:HzWC3XjadkyeuBZxkfAFNY20UVvle0YD51I6zf6RKlU=
|
||||
github.com/tetafro/godot v1.3.2/go.mod h1:ah7jjYmOMnIjS9ku2krapvGQrFNtTLo9Z/qB3dGU1eU=
|
||||
github.com/theupdateframework/go-tuf v0.0.0-20201230183259-aee6270feb55 h1:Zn+mA4qTRyao2Petd+YovKaFOUuxDj158kqCIqvwTow=
|
||||
github.com/theupdateframework/go-tuf v0.0.0-20201230183259-aee6270feb55/go.mod h1:L+uU/NRFK/7h0NYAnsmvsX9EghDB5QVCcHCIrK2h5nw=
|
||||
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94 h1:ig99OeTyDwQWhPe2iw9lwfQVF1KB3Q4fpP3X7/2VBG8=
|
||||
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tomarrell/wrapcheck v0.0.0-20200807122107-df9e8bcb914d/go.mod h1:yiFB6fFoV7saXirUGfuK+cPtUh4NX/Hf5y2WC2lehu0=
|
||||
github.com/tomarrell/wrapcheck v0.0.0-20201130113247-1683564d9756 h1:zV5mu0ESwb+WnzqVaW2z1DdbAP0S46UtjY8DHQupQP4=
|
||||
github.com/tomarrell/wrapcheck v0.0.0-20201130113247-1683564d9756/go.mod h1:yiFB6fFoV7saXirUGfuK+cPtUh4NX/Hf5y2WC2lehu0=
|
||||
github.com/tommy-muehle/go-mnd v1.3.1-0.20201008215730-16041ac3fe65 h1:Y0bLA422kvb32uZI4fy/Plop/Tbld0l9pSzl+j1FWok=
|
||||
github.com/tommy-muehle/go-mnd v1.3.1-0.20201008215730-16041ac3fe65/go.mod h1:T22e7iRN4LsFPZGyRLRXeF+DWVXFuV9thsyO7NjbbTI=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA=
|
||||
github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||
github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg=
|
||||
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/uudashr/gocognit v1.0.1 h1:MoG2fZ0b/Eo7NXoIwCVFLG5JED3qgQz5/NEE+rOsjPs=
|
||||
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA=
|
||||
github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
|
||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201109165425-215b40eba54c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201011145850-ed2f50202694/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201121010211-780cb80bd7fb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2 h1:vEtypaVub6UvKkiXZ2xx9QIvp9TL7sI7xp7vdi2kezA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.6 h1:W18jzjh8mfPez+AwGLxmOImucz/IFjpNlrKVnaj2YVc=
|
||||
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475 h1:5ZmJGYyuTlhdlIpRxSFhdJqkXQweXETFCEaLhRAX3e8=
|
||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 h1:kAREL6MPwpsk1/PQPFD3Eg7WAQR5mPTWZJaBiG5LDbY=
|
||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7/go.mod h1:HGC5lll35J70Y5v7vCGb9oLhHoScFwkHDJm/05RdSTc=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
92
orbit/pkg/certificate/certificate.go
Normal file
92
orbit/pkg/certificate/certificate.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Package certificate contains functions for handling TLS certificates.
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// FetchPEM retrieves the certificate chain presented by the server listening at
|
||||
// hostname in PEM format.
|
||||
//
|
||||
// Adapted from https://stackoverflow.com/a/46735876/491710
|
||||
func FetchPEM(hostname string) ([]byte, error) {
|
||||
conn, err := tls.Dial("tcp", hostname, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "dial server to fetch PEM")
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var b bytes.Buffer
|
||||
for _, cert := range conn.ConnectionState().PeerCertificates {
|
||||
err := pem.Encode(&b, &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Raw,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "encode PEM")
|
||||
}
|
||||
}
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// LoadPEM loads certificates from a PEM file and returns a cert pool containing
|
||||
// the certificates.
|
||||
func LoadPEM(path string) (*x509.CertPool, error) {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
contents, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "read certificate file")
|
||||
}
|
||||
|
||||
if ok := pool.AppendCertsFromPEM(contents); !ok {
|
||||
return nil, errors.Errorf("no valid ceritificates found in %s", path)
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
// ValidateConnection checks that a connection can be successfully established
|
||||
// to the server URL using the cert pool provided. The validation performed is
|
||||
// not sufficient to verify authenticity of the server, but it can help to catch
|
||||
// certificate errors and provide more detailed messages to users.
|
||||
func ValidateConnection(pool *x509.CertPool, fleetURL string) error {
|
||||
parsed, err := url.Parse(fleetURL)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "parse url")
|
||||
}
|
||||
conn, err := tls.Dial("tcp", parsed.Host, &tls.Config{
|
||||
ClientCAs: pool,
|
||||
InsecureSkipVerify: true,
|
||||
VerifyConnection: func(state tls.ConnectionState) error {
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
return errors.New("no peer certificates")
|
||||
}
|
||||
|
||||
cert := state.PeerCertificates[0]
|
||||
if _, err := cert.Verify(x509.VerifyOptions{
|
||||
DNSName: parsed.Hostname(),
|
||||
Roots: pool,
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "verify certificate")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "dial for validate")
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
70
orbit/pkg/certificate/certificate_test.go
Normal file
70
orbit/pkg/certificate/certificate_test.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package certificate
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFetchPEMInvalidHostname(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := FetchPEM("foobar")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestFetchPEM(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
certPath := filepath.Join("testdata", "test.crt")
|
||||
keyPath := filepath.Join("testdata", "test.key")
|
||||
expectedCert, err := ioutil.ReadFile(certPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var port int
|
||||
go func() {
|
||||
// Assign any available port
|
||||
listener, err := net.Listen("tcp", "localhost:0")
|
||||
require.NoError(t, err)
|
||||
port = listener.Addr().(*net.TCPAddr).Port
|
||||
defer listener.Close()
|
||||
|
||||
err = http.ServeTLS(listener, nil, certPath, keyPath)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
// Sleep to allow the goroutine to run and start the server.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
pem, err := FetchPEM("localhost:" + strconv.Itoa(port))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedCert, pem)
|
||||
}
|
||||
|
||||
func TestLoadPEM(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pool, err := LoadPEM(filepath.Join("testdata", "test.crt"))
|
||||
require.NoError(t, err)
|
||||
assert.True(t, len(pool.Subjects()) > 0)
|
||||
}
|
||||
|
||||
func TestLoadErrorNoCertificates(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := LoadPEM(filepath.Join("testdata", "empty.crt"))
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestLoadErrorMissingFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := LoadPEM(filepath.Join("testdata", "invalid_path"))
|
||||
require.Error(t, err)
|
||||
}
|
||||
1
orbit/pkg/certificate/testdata/empty.crt
vendored
Normal file
1
orbit/pkg/certificate/testdata/empty.crt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
no cert contained in here
|
||||
28
orbit/pkg/certificate/testdata/test.crt
vendored
Normal file
28
orbit/pkg/certificate/testdata/test.crt
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIErjCCApYCCQCj1g+mDUoEQjANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5s
|
||||
b2NhbGhvc3Q6ODU0MzAeFw0yMDEyMTcyMzA4MzJaFw00ODA1MDMyMzA4MzJaMBkx
|
||||
FzAVBgNVBAMMDmxvY2FsaG9zdDo4NTQzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
|
||||
MIICCgKCAgEAyAThP2RVyrZd1hnrjoz0f87dT3xC6PDFMf+SG55A8YhtvylHG3Ph
|
||||
Xq8TZ6oFhWmqLjllAGKlfJRcTnqY9f3mtDFvNt5yTu7Ot32VL5viZobp5fw0Wfcu
|
||||
G2rKENZ5/3r6pE5iD2oRU5CxGsdnjRhKfVwK1ayF60hY23xY5TE+RVnyvuqsRMWU
|
||||
2vTD3rx8k9g1/kJwkujG0eL3K3NslamM5yWxsRdUk0MCQDGlr176zqToUIhMqQLR
|
||||
vkKw1hm+m9OQHHn3pucUdPbQynWMVEUKCCF19wtqW2rUW9POgkPD4cPf3D999rvK
|
||||
Y08XhzXmS74bAbDXBKHCFAQFFJUseSNEDjIBn/gfMH6KX0x1kiRixj/hdel9VdkO
|
||||
dUl/AScfOTg71DxjQDbp/EW/diufNZwadXbBjiKeys8Zi9ISt4WQmXc08FTRCb9T
|
||||
6rghenH2UBOT9zZn8yIoq38WfZ2dUmJPhIdEh27GOa/ets1psNg8lh+bEDKmDstd
|
||||
6/SXlHR4Pfhqy4cqppG09Z/Hl6fgyr5U4/sdWN4gm/ZkbTGgPGJ2fQEuuuEsFXC2
|
||||
US9aJcEC4rw103yNr7/4P9u3U+iHGbq8wOFCEtf4p4mi/9S1Txsupnd/hbla2k2W
|
||||
vNYhzqbbatf/A6ahc8Vee8x+Zd/fOcuk0khb8evq/TnBJIsw67aXPEECAwEAATAN
|
||||
BgkqhkiG9w0BAQsFAAOCAgEAhYWB/mrNLZi1zru0W1gmfuSE3wBrN0G5Ce/alNi6
|
||||
MwHoQiBCxZmmYndbXEaXtwhsLoV7mlOYdYr1DiK94usT8g9kj4kymYsl0jPdM+1w
|
||||
PPARBoPeqpHQBSYbAaB9Iqx1Wc/I5g/kwgrtoPM1Z3y7f2277BqeoxkwhVbC0aLC
|
||||
cYkk+tzK4+sPfrW/onLfVuKsrVcoFnDsg/aa3rKdTcTsTwrJUF5S1hiK2CSkpJbx
|
||||
60C4Zu7fUYFHbjcorsy+dSVX26+56sazS8o6Gpa+orPjLybFazCX806gPWmKQW48
|
||||
xkIpIJG0nocvqesUBEbJX/FKuAJ/yLl5P9uGJtjNlK4j/uhm9f5M49KoTSSAQ4A1
|
||||
Wm/2cBVPokGJ1c+SVGPHgu6QdBvVXznG/CI42cKwj4T0H4wwcah4lw3R9Vy6EqNW
|
||||
13h20qs/2MnZ0GL2po3M9vYlV6x4dQPz+lXVlez2h1EkI4muffvz+Ql4zgkkAdUO
|
||||
f+H+eC+m8Hf3/dtWhXdgoOFvMOROQIgYU+W+H6TXrGKwd7n/UkvxJEgNgXfcXlug
|
||||
qICkuCVW2Z9BaIZiW9X12aNlEPUIgTxbNSYv/gZRXZ54St5DKmUJd5vuV1gm+xxA
|
||||
+8Lr0uIAxknZGE1OAlADxwwXSJGjAC0m/13X/L/Kb5kd4DyiQvnyVcFfkwSp/xbd
|
||||
Ihw=
|
||||
-----END CERTIFICATE-----
|
||||
52
orbit/pkg/certificate/testdata/test.key
vendored
Normal file
52
orbit/pkg/certificate/testdata/test.key
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDIBOE/ZFXKtl3W
|
||||
GeuOjPR/zt1PfELo8MUx/5IbnkDxiG2/KUcbc+FerxNnqgWFaaouOWUAYqV8lFxO
|
||||
epj1/ea0MW823nJO7s63fZUvm+Jmhunl/DRZ9y4basoQ1nn/evqkTmIPahFTkLEa
|
||||
x2eNGEp9XArVrIXrSFjbfFjlMT5FWfK+6qxExZTa9MPevHyT2DX+QnCS6MbR4vcr
|
||||
c2yVqYznJbGxF1STQwJAMaWvXvrOpOhQiEypAtG+QrDWGb6b05Acefem5xR09tDK
|
||||
dYxURQoIIXX3C2pbatRb086CQ8Phw9/cP332u8pjTxeHNeZLvhsBsNcEocIUBAUU
|
||||
lSx5I0QOMgGf+B8wfopfTHWSJGLGP+F16X1V2Q51SX8BJx85ODvUPGNANun8Rb92
|
||||
K581nBp1dsGOIp7KzxmL0hK3hZCZdzTwVNEJv1PquCF6cfZQE5P3NmfzIiirfxZ9
|
||||
nZ1SYk+Eh0SHbsY5r962zWmw2DyWH5sQMqYOy13r9JeUdHg9+GrLhyqmkbT1n8eX
|
||||
p+DKvlTj+x1Y3iCb9mRtMaA8YnZ9AS664SwVcLZRL1olwQLivDXTfI2vv/g/27dT
|
||||
6IcZurzA4UIS1/iniaL/1LVPGy6md3+FuVraTZa81iHOpttq1/8DpqFzxV57zH5l
|
||||
3985y6TSSFvx6+r9OcEkizDrtpc8QQIDAQABAoICAQCQau2Tzsce+TOhfc+VenKi
|
||||
wWMLnAXXmNhYtFXzOP1dJ4tOBejAipfDvJc/NwSLgnKMs4YYlCE2ZQyM4XoFyxBD
|
||||
NJo/PLe+BDEfTT5lRKFgM7M4CjRmbNFOnHlPVPx7GXlVHv8wb/4YnxUw5579wfcu
|
||||
skFkbA+5hOAbgZnRyg9TFZHuhRRjB2HmAepWrTMpsGezsJcFi6raKo0aQ1m4adZB
|
||||
ova8jcLUHQLIBNDDYcmnYGwYkYEAWvfD7vUtcSMt8cBQv0Ovr5MWrIquU9dBlSOc
|
||||
IUTCXeyqGuU/dBvb9D+/h6TfqrwxZP/JWnb7elBQie+H9f4Z6w1tVEWyyc51nJ/q
|
||||
dQk63N3Kji+hXetR6tjAADwMKvJKn1Fd4xzgPt0OPHQu1J+kt2LXpdt78ynoPivz
|
||||
eH1Ejf43YzSf6Zm8zisyfgDt9XBauNjosrDhrzvtMNAIYEA1jkZ0f0YUjHT4lltF
|
||||
K1R9nl3MQGEQ1AfG4XaCfMRwTSKgxf8HWNYxWexQGvZRz62QKW4neLWjPYBM+jgt
|
||||
em70efwjtXyy34rE7t4ItkphyvpnN1WFKLCve/Jpg5Ds4QzImqnHGnDIIQ8F+cNS
|
||||
TXFblNOHcB5MgQI1I4Z08Ibq6a/2nYEpZZtBElug22hTTuYdo5vfiMTPsFdOosV+
|
||||
acbzawQScBCVUd5WhI1qnQKCAQEA6RJW9LbrUXxMEfZEbt9ucvS4Q0gkR/Fo7zGo
|
||||
nEHc1MQlW93FPXOo/af41pN93V4c0k2M7cmB2iuGdqgxaPlRJDDHLFx4lF9gFNIS
|
||||
GoxwKalMl+mTZrt9+f8zg/R/rQIwxpvYmpFhmtUbYHO37f+bwkMsZdbhw4bLEHSA
|
||||
aPJup6LWJx6D+WnwgVABx2b4kaKGxlxVWBwr1a+NG0H18G4gnCgu3Cpx3Yaw/+cN
|
||||
jLDy3ypPnkrVW72dyj24WtIfZc4rk9UkAJbwMtJXshYwbx+Ha0UD03dhlD1R50iz
|
||||
ldvxSPkygNuJvgK34mFMS/Bga6PjRvlB9KHf7AeyE8ePsW56pwKCAQEA27Ildo1W
|
||||
fmqS5fHXZ90lVzY37ZjKT9+OWsYq3dDievHtjdCXmsALAoXQKwE27i/C+a4AKSuU
|
||||
SjsHCdV345ne0M3PrFR5OiCLVSRfqyBAetXkBic37W/xj7PTcBaWj/6u6iwgcQrq
|
||||
/rEwXWdPCtT1Xp7ZI5kq38CuH0yAmQqnhzybTXj4g+f3mvgK+jxcG39KAJqQk18b
|
||||
emXwsIwCbohSlvy7i5TQYJehy+IWRQFeD8oLkzQRtnGv+faht2xebi3i5SzJES1V
|
||||
keUIgOTYIED03YpNyOWFNqBjRgiWHvfqcKOAP9CiUcsIqDNgTZoUHKcafuMDfLIc
|
||||
a8hh3wsttHk21wKCAQEAgUBiaKNoLHA531wq5zGRFr8P0IAZXqxJ/RwU3VLJHFUK
|
||||
Qr/hugqCFOkp3hU9H54pbZcEKHovQMYSc4sim4xnqyJB0iAV3nJl3iYBVCe9q9zv
|
||||
VO97huVDH3ifIPZeN5uXYQzcOhuVfT/hRsPlpF60Ci8mV/Oqj7wYsK9q5shLBTwQ
|
||||
dvE9TvupHI0571LzVhBDnY1m5s34oTARmKQjt4nbG75M/lureaZQUhnYMcWPaqMU
|
||||
NDxbxdyJvLYtnnAYaWTEVd9Fb+5Elmp/p4sWoQljF+HWFVeHDaZT3Wc78Edgr49C
|
||||
qqMU6AKp5yj+Hr7XOdpRF/Ly2K9MdqBt6PoqfcM+0QKCAQEAlcIQK4oZBb+cN0bt
|
||||
8Q0coSCCa2IVtvDLVzFykxYK7IfxyRQB5Ck42BVjW41OZnsES7LCxU3BngAywg9T
|
||||
1EBVVLyW7f//SxJYKEfNNxebHKCk+VTOmPoQDkckwGmFZM7VaSd/Tc+FdOxP7bu4
|
||||
c8fLIx7hIZUNVs0/ZHJ/ztMmc9dqfhsSPMhpTqf6w61VlCDmzxaNqwfP8VWABrjK
|
||||
B5LByc6qAXIFwXJbhmGtkIhoGBdAYK3DzTweGyR2BFTI4g5BdrtarGzcwcEAVOyq
|
||||
LwJYaJQYhfN/JUebpGfB/YY9t16c0+NiYqMmWZH7+aooP4fhVdFl1SCMoyRLIwG5
|
||||
vCZMrQKCAQBamPky1dKIfOehquwmcQnNZF0Wh/7tfJv2xhX6FswbPhFhAAepXmsY
|
||||
4NKqqEy8pXgZ/7vXLawGAgQr2taPF4hx9QSmUNefZZpgOVVyfZgskfpMi8NcYGex
|
||||
Ap4C52EiSWUooj+7vsaTvzIkUxnEUy18aRfxhTKkHcbv+d9bYOr6/BZrpetpFQEW
|
||||
DzQ5dETUDhTaCyJknj3SLZRqoxOcyDkgUzFY9fJbzdlUYTtMLgnRmA37xTVuWfDR
|
||||
MxLnssq56iONJylCox0sNkdhFe1f4JiLYyStHzerhCTNtSD53c6Q5hWmCFJXwllO
|
||||
rdcA+RwiWNrxDmbPFs7HuiIft5MwbmdE
|
||||
-----END PRIVATE KEY-----
|
||||
19
orbit/pkg/constant/constant.go
Normal file
19
orbit/pkg/constant/constant.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package constant
|
||||
|
||||
const (
|
||||
// DefaultDirMode is the default file mode to apply to created directories.
|
||||
DefaultDirMode = 0o755
|
||||
// DefaultFileMode is the default file mode to apply to created files.
|
||||
DefaultFileMode = 0o600
|
||||
)
|
||||
|
||||
// ExecutableExtension returns the extension used for executables on the
|
||||
// provided platform.
|
||||
func ExecutableExtension(platform string) string {
|
||||
switch platform {
|
||||
case "windows":
|
||||
return ".exe"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
5
orbit/pkg/constant/constant_darwin.go
Normal file
5
orbit/pkg/constant/constant_darwin.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package constant
|
||||
|
||||
const (
|
||||
PlatformName = "macos"
|
||||
)
|
||||
5
orbit/pkg/constant/constant_linux.go
Normal file
5
orbit/pkg/constant/constant_linux.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package constant
|
||||
|
||||
const (
|
||||
PlatformName = "linux"
|
||||
)
|
||||
9
orbit/pkg/constant/constant_notwindows.go
Normal file
9
orbit/pkg/constant/constant_notwindows.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
//+build !windows
|
||||
|
||||
package constant
|
||||
|
||||
const (
|
||||
// DefaultExecutableMode is the default file mode to apply to created
|
||||
// executable files.
|
||||
DefaultExecutableMode = 0o755
|
||||
)
|
||||
34
orbit/pkg/constant/constant_windows.go
Normal file
34
orbit/pkg/constant/constant_windows.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//+build windows
|
||||
|
||||
package constant
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
PlatformName = "windows"
|
||||
// DefaultExecutableMode is the default file mode to apply to created
|
||||
// executable files. For Windows this doesn't do anything besides setting
|
||||
// read-only. See https://golang.org/pkg/os/#Chmod.
|
||||
DefaultExecutableMode = 0o700
|
||||
)
|
||||
|
||||
var (
|
||||
// These identifiers can be found in
|
||||
// https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/security-identifiers-in-windows
|
||||
// and are used in the same fashion as in osquery. See
|
||||
// https://github.com/osquery/osquery/blob/d2be385d71f401c85872f00d479df8f499164c5a/tools/deployment/chocolatey/tools/osquery_utils.ps1.
|
||||
SystemSID = mustSID("S-1-5-18")
|
||||
AdminSID = mustSID("S-1-5-32-544")
|
||||
UserSID = mustSID("S-1-5-32-545")
|
||||
)
|
||||
|
||||
func mustSID(identifier string) *windows.SID {
|
||||
sid, err := windows.StringToSid(identifier)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "create sid"))
|
||||
}
|
||||
return sid
|
||||
}
|
||||
97
orbit/pkg/database/database.go
Normal file
97
orbit/pkg/database/database.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
compactionInterval = 5 * time.Minute
|
||||
// This is the discard ratio recommended in Badger docs
|
||||
// (https://pkg.go.dev/github.com/dgraph-io/badger#DB.RunValueLogGC)
|
||||
compactionDiscardRatio = 0.5
|
||||
)
|
||||
|
||||
// BadgerDB is a wrapper around the standard badger.DB that provides a
|
||||
// background compaction routine.
|
||||
type BadgerDB struct {
|
||||
*badger.DB
|
||||
closeChan chan struct{}
|
||||
}
|
||||
|
||||
// Open opens (initializing if necessary) a Badger database at the specified
|
||||
// path. Users must close the DB with Close().
|
||||
func Open(path string) (*BadgerDB, error) {
|
||||
// DefaultOptions sets synchronous writes to true (maximum data integrity).
|
||||
// TODO implement logging?
|
||||
db, err := badger.Open(badger.DefaultOptions(path).WithLogger(nil))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "open badger %s", path)
|
||||
}
|
||||
|
||||
b := &BadgerDB{DB: db}
|
||||
b.startBackgroundCompaction()
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// OpenTruncate opens (initializing and/or truncating if necessary) a Badger
|
||||
// database at the specified path. Users must close the DB with Close().
|
||||
//
|
||||
// Prefer Open in the general case, but after a bad shutdown it may be necessary
|
||||
// to call OpenTruncate. This may cause data loss. Detect this situation by
|
||||
// looking for badger.ErrTruncateNeeded.
|
||||
func OpenTruncate(path string) (*BadgerDB, error) {
|
||||
// DefaultOptions sets synchronous writes to true (maximum data integrity).
|
||||
// TODO implement logging?
|
||||
db, err := badger.Open(badger.DefaultOptions(path).WithLogger(nil).WithTruncate(true))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "open badger with truncate %s", path)
|
||||
}
|
||||
|
||||
b := &BadgerDB{DB: db}
|
||||
b.startBackgroundCompaction()
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// startBackgroundCompaction starts a background loop that will call the
|
||||
// compaction method on the database. Badger does not do this automatically, so
|
||||
// we need to be sure to do so here (or elsewhere).
|
||||
func (b *BadgerDB) startBackgroundCompaction() {
|
||||
if b.closeChan != nil {
|
||||
panic("background compaction already running")
|
||||
}
|
||||
b.closeChan = make(chan struct{})
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(compactionInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-b.closeChan:
|
||||
return
|
||||
|
||||
case <-ticker.C:
|
||||
if err := b.DB.RunValueLogGC(compactionDiscardRatio); err != nil && !errors.Is(err, badger.ErrNoRewrite) {
|
||||
log.Error().Err(err).Msg("compact badger")
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// stopBackgroundCompaction stops the background compaction routine.
|
||||
func (b *BadgerDB) stopBackgroundCompaction() {
|
||||
b.closeChan <- struct{}{}
|
||||
b.closeChan = nil
|
||||
}
|
||||
|
||||
// Close closes the database connection and releases the associated resources.
|
||||
func (b *BadgerDB) Close() error {
|
||||
b.stopBackgroundCompaction()
|
||||
return b.DB.Close()
|
||||
}
|
||||
80
orbit/pkg/database/database_test.go
Normal file
80
orbit/pkg/database/database_test.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDatabase(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "orbit-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Open and write
|
||||
db, err := Open(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.Update(func(tx *badger.Txn) error {
|
||||
require.NoError(t, tx.Set([]byte("key"), []byte("value")))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.Close())
|
||||
|
||||
// Reopen and read
|
||||
db, err = Open(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.View(func(tx *badger.Txn) error {
|
||||
item, err := tx.Get([]byte("key"))
|
||||
require.NoError(t, err)
|
||||
err = item.Value(func(val []byte) error {
|
||||
assert.Equal(t, []byte("value"), val)
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
|
||||
func TestCompactionPanic(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "orbit-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
db, err := Open(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to start the compaction routine again
|
||||
assert.Panics(t, func() { db.startBackgroundCompaction() })
|
||||
}
|
||||
|
||||
func TestCompactionRestart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "orbit-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
db, err := Open(tmpDir)
|
||||
require.NoError(t, err)
|
||||
go func() {
|
||||
require.NoError(t, db.Close())
|
||||
}()
|
||||
|
||||
db.stopBackgroundCompaction()
|
||||
|
||||
assert.NotPanics(t, func() { db.startBackgroundCompaction() })
|
||||
}
|
||||
211
orbit/pkg/insecure/proxy.go
Normal file
211
orbit/pkg/insecure/proxy.go
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
// Package insecure provides an insecure (if it were not obvious yet) TLS proxy
|
||||
// that can be used for testing osquery enrollment with a Fleet (or other TLS)
|
||||
// server in non-production environments.
|
||||
//
|
||||
// Functions in this package are NOT SUITABLE FOR PRODUCTION ENVIRONMENTS!
|
||||
package insecure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// ServerCert is the certificate used by the proxy server.
|
||||
ServerCert = `-----BEGIN CERTIFICATE-----
|
||||
MIICpDCCAYwCCQCPnw3uINXlozANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
|
||||
b2NhbGhvc3QwHhcNMjAxMjE5MDA0MDA0WhcNNDgwNTA1MDA0MDA0WjAUMRIwEAYD
|
||||
VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1
|
||||
GuqbXIo767H1dPM9KS7Uz6bU3Jzh/4e5fxmgxTz2GY76UiKhBKvlWIy2PsFMKpQ1
|
||||
kd3/MyANoOcUkdolPAX/6jMZc9qhlRaG80MqgZuBzX3KHnCnFN9vin1wOrTlyboW
|
||||
NLjKCmKTCpa0knuya9hgOwCJ1cFMFByC29qRvYKtisQxRbpy/d/jN14dXsGeQiZW
|
||||
KU6ncmFPBH8+uTnrQq4A3UBFMOu5C+Uk+hCSLNMu4ZbAUR41m0LpR5OaWk1t0q2O
|
||||
ZbDg4zkSJzbBNeiVe+vCbKtevqtRgqAi4u4EGdasnlhEJ/UPfF9lvqCd1iRw7M9u
|
||||
quPlsJs6tE4GFBpIUUMBAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA5KnpFoTnKW
|
||||
B04G42v6a2AkY/ENEgoMhKr6JBeRkRKF6Itatiotb/RClgRYlDUn+ljow8/Tyds4
|
||||
qqMl/MzjbbwI4xNcu9t+0bG2zmJj6ON4mbRH+GnBPX+t50/1eKSoPjtHDyT/UAbx
|
||||
q3jyXp0nObaRzDqmYK/OUVg7vhAxQqQ9Cvvk819Ar8wFZGjE9Bc2YDObyCVQWCZz
|
||||
qIfzr/Qh46tq0o+KdlaV2oHy4VLrLOFXeD5MKf6A7aOP7h9Yy9ywnScrobaSXwd8
|
||||
kS/PZzVeJtwvKf+c1tBiJxHix2vLiFtS5IKdhNGKNvMyQNWgq046iTNeVJkxo+Qb
|
||||
YP4a5WpD+aw=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
// serverKey is the corresponding private key. This key is compromised by
|
||||
// being in the source code, rendering any connection using this cert
|
||||
// insecure.
|
||||
serverKey = `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1GuqbXIo767H1
|
||||
dPM9KS7Uz6bU3Jzh/4e5fxmgxTz2GY76UiKhBKvlWIy2PsFMKpQ1kd3/MyANoOcU
|
||||
kdolPAX/6jMZc9qhlRaG80MqgZuBzX3KHnCnFN9vin1wOrTlyboWNLjKCmKTCpa0
|
||||
knuya9hgOwCJ1cFMFByC29qRvYKtisQxRbpy/d/jN14dXsGeQiZWKU6ncmFPBH8+
|
||||
uTnrQq4A3UBFMOu5C+Uk+hCSLNMu4ZbAUR41m0LpR5OaWk1t0q2OZbDg4zkSJzbB
|
||||
NeiVe+vCbKtevqtRgqAi4u4EGdasnlhEJ/UPfF9lvqCd1iRw7M9uquPlsJs6tE4G
|
||||
FBpIUUMBAgMBAAECggEBAKUhQcEfA7vXEJBqbk7Z+iV4oPl9nl5CjBKK3WdF8GvE
|
||||
qiV8Nq7yf3nC36pcVguI11JxCiXjC9rhV1HeGzXQIPhTJvySMksakUvDCv765jvY
|
||||
jlV4o+b0lTYy5GUsYj0TTmVo9QTjqzW/deJ3nen1g3la0wbarEEeJVD7/bLdRQXN
|
||||
9DcFtKpC+to0N7RE2cFLGIgWcU/W6IF+DoLkVKdd0h2EZ4FawRtythkfC2Z+B1W/
|
||||
ayz645XIQcA2sI2z4G2CqR/k3gmv6eGzoQgTN46z2lYqPFMD7v8hwv9Z/LCIghF9
|
||||
iJQy8Uxy8zRjmCDNP0oWRrlIUh2abqhgLlvvYsFJ3QECgYEA4Z0FJmsgf65NlBzQ
|
||||
g5+x9pQLZX0tEpeuDZT+W9i+7u/d1nkISmD+urL+f1o5Jc8dg0QG9Y6XFodIsp8W
|
||||
s6wbCcBR+V7q76fJONRLnh5mHNwBvvVGg5Iy/Bmc8gTboG7F4yJnrvCYeE4gIVXt
|
||||
4c5c7B1hOC/eyXIGng5bbKwtCAkCgYEAzX9JSOwWlt3Y75SCL8d3WSEBVqgaH1UZ
|
||||
4lSZ/Bi1F7uUToJGMnH2XinZ0tO7erPtisl/S9SZhMmIFho+p/23Mz4SWtLGzIBu
|
||||
q8Zoo4gfCk2cP5YkidKad1Kc3ZG4LWQCiRr7+HYYmTXaUWe5PNznlp36IFliPlkK
|
||||
Q2ztNOpq8TkCgYBa4SQ08IwbwnuPgPfhPU+zcrkQfZbNWXoMEItRNgLbPpYOkZxs
|
||||
UZvqWrW3WQGSIFbUDG/9NB3aPk5jXUAIyffuOqEKoVhjhyPAF4wKOlaJo3m0kRqB
|
||||
Xz/YWvzkZF6Pxm9B6hb32gSg2V+J7hIvli/KEJ+bwXStkpflzQS4xrYw+QKBgGqH
|
||||
T8Bj0xoGi403WX3XU4F64KzBnDkd7rsrzF+pl0dkUG+ajTVdarBJ1ce7R3dGix/l
|
||||
cP4oiiUSLF/43v5LQotn5C/9EF23PqgBxQDxcdXvgc5c0Tg5WyX8R6F9BxNQwxe8
|
||||
S170KbBTAIgu0xJAGjY0UxQuAgX8NpvZfeZul13RAoGARVSBIo5MDodDw25j4fc7
|
||||
YIC5ppouCT4VTz6IUOxYyw8TQBBm5Wes62JJ+9yTLLfIRNnkDkVwSX7Q3blva+1W
|
||||
1lFBG1WzNhTRo0im9lRyVCGp2ZSm2UxsJS9jP1J6bkavkjK/jaPOC9J6dmWOZBWm
|
||||
yJY8h/0WhN9XPcXFa4Qmfu0=
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
)
|
||||
|
||||
// TLSProxy is the insecure TLS proxy implementation. This type should only be
|
||||
// initialized via NewTLSProxy.
|
||||
type TLSProxy struct {
|
||||
// Port is the port the TLS proxy is listening on (always on localhost).
|
||||
Port int
|
||||
|
||||
listener net.Listener
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
// NewTLSProxy creates a new proxy implementation targeting the provided
|
||||
// hostname.
|
||||
func NewTLSProxy(targetURL string) (*TLSProxy, error) {
|
||||
cert, err := tls.X509KeyPair([]byte(ServerCert), []byte(serverKey))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load keypair")
|
||||
}
|
||||
cfg := &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
|
||||
// Assign any available port
|
||||
listener, err := tls.Listen("tcp", "localhost:0", cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "bind localhost")
|
||||
}
|
||||
|
||||
addr, ok := listener.Addr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, errors.New("listener is not *net.TCPAddr")
|
||||
}
|
||||
|
||||
handler, err := newProxyHandler(targetURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "make proxy handler")
|
||||
}
|
||||
|
||||
proxy := &TLSProxy{
|
||||
Port: addr.Port,
|
||||
listener: listener,
|
||||
server: &http.Server{Handler: handler},
|
||||
}
|
||||
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
// InsecureServeTLS will begin running the TLS proxy.
|
||||
func (p *TLSProxy) InsecureServeTLS() error {
|
||||
if p.listener == nil || p.server == nil {
|
||||
return errors.New("listener and handler must not be nil -- initialize TLSProxy via NewTLSProxy")
|
||||
}
|
||||
|
||||
err := p.server.Serve(p.listener)
|
||||
return errors.Wrap(err, "servetls returned")
|
||||
}
|
||||
|
||||
// Close the server and associated listener. The server may not be reused after
|
||||
// calling Close().
|
||||
func (p *TLSProxy) Close() error {
|
||||
// err := p.listener.Close()
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "close listener")
|
||||
// }
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
return p.server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
func newProxyHandler(targetURL string) (*httputil.ReverseProxy, error) {
|
||||
target, err := url.Parse(targetURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "parse target url")
|
||||
}
|
||||
|
||||
reverseProxy := &httputil.ReverseProxy{
|
||||
Director: func(req *http.Request) {
|
||||
req.Host = target.Host
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL)
|
||||
},
|
||||
}
|
||||
// Adapted from http.DefaultTransport
|
||||
reverseProxy.Transport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
|
||||
return reverseProxy, nil
|
||||
}
|
||||
|
||||
// Copied from Go source
|
||||
// https://go.googlesource.com/go/+/go1.15.6/src/net/http/httputil/reverseproxy.go#114
|
||||
func joinURLPath(a, b *url.URL) (path, rawpath string) {
|
||||
if a.RawPath == "" && b.RawPath == "" {
|
||||
return singleJoiningSlash(a.Path, b.Path), ""
|
||||
}
|
||||
// Same as singleJoiningSlash, but uses EscapedPath to determine
|
||||
// whether a slash should be added
|
||||
apath := a.EscapedPath()
|
||||
bpath := b.EscapedPath()
|
||||
aslash := strings.HasSuffix(apath, "/")
|
||||
bslash := strings.HasPrefix(bpath, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a.Path + b.Path[1:], apath + bpath[1:]
|
||||
case !aslash && !bslash:
|
||||
return a.Path + "/" + b.Path, apath + "/" + bpath
|
||||
}
|
||||
return a.Path + b.Path, apath + bpath
|
||||
}
|
||||
|
||||
// Copied from Go source
|
||||
// https://go.googlesource.com/go/+/go1.15.6/src/net/http/httputil/reverseproxy.go#102
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash:
|
||||
return a + "/" + b
|
||||
}
|
||||
return a + b
|
||||
}
|
||||
16
orbit/pkg/insecure/proxy_test.go
Normal file
16
orbit/pkg/insecure/proxy_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package insecure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestProxy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
proxy, err := NewTLSProxy("localhost")
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, proxy.Port)
|
||||
}
|
||||
32
orbit/pkg/osquery/flags.go
Normal file
32
orbit/pkg/osquery/flags.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package osquery
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
)
|
||||
|
||||
// FleetFlags is the set of flags to pass to osquery when connecting to Fleet.
|
||||
func FleetFlags(fleetURL *url.URL) []string {
|
||||
hostname, prefix := fleetURL.Host, fleetURL.Path
|
||||
return []string{
|
||||
"--tls_hostname=" + hostname,
|
||||
"--enroll_tls_endpoint=" + path.Join(prefix, "/api/v1/osquery/enroll"),
|
||||
"--config_plugin=tls",
|
||||
"--config_tls_endpoint=" + path.Join(prefix, "/api/v1/osquery/config"),
|
||||
// Osquery defaults config_refresh to 0 which is probably not ideal for
|
||||
// a client connected to Fleet. Users can always override this in the
|
||||
// config they serve via Fleet.
|
||||
"--config_refresh=60",
|
||||
"--disable_distributed=false",
|
||||
"--distributed_plugin=tls",
|
||||
"--distributed_tls_max_attempts=10",
|
||||
"--distributed_tls_read_endpoint=" + path.Join(prefix, "/api/v1/osquery/distributed/read"),
|
||||
"--distributed_tls_write_endpoint=" + path.Join(prefix, "/api/v1/osquery/distributed/write"),
|
||||
"--logger_plugin=tls",
|
||||
"--logger_tls_endpoint=" + path.Join(prefix, "/api/v1/osquery/log"),
|
||||
"--disable_carver=false",
|
||||
"--carver_start_endpoint=" + path.Join(prefix, "/api/v1/osquery/carve/begin"),
|
||||
"--carver_continue_endpoint=" + path.Join(prefix, "/api/v1/osquery/carve/block"),
|
||||
"--carver_block_size=2000000",
|
||||
}
|
||||
}
|
||||
125
orbit/pkg/osquery/osquery.go
Normal file
125
orbit/pkg/osquery/osquery.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// package osquery implements a runtime for osqueryd.
|
||||
package osquery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/process"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Runner is a specialized runner for osquery. It is designed with Execute and
|
||||
// Interrupt functions to be compatible with oklog/run.
|
||||
type Runner struct {
|
||||
proc *process.Process
|
||||
cmd *exec.Cmd
|
||||
cancel func()
|
||||
}
|
||||
|
||||
// NewRunner creates a new osquery runner given the provided functional options.
|
||||
func NewRunner(path string, options ...func(*Runner) error) (*Runner, error) {
|
||||
r := &Runner{}
|
||||
|
||||
cmd := exec.Command(path)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
r.cmd = cmd
|
||||
r.proc = process.NewWithCmd(cmd)
|
||||
|
||||
for _, option := range options {
|
||||
err := option(r)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "apply option")
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// WithFlags adds additional flags to the osqueryd invocation.
|
||||
func WithFlags(flags []string) func(*Runner) error {
|
||||
return func(r *Runner) error {
|
||||
r.cmd.Args = append(r.cmd.Args, flags...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithEnv adds additional environment variables to the osqueryd invocation.
|
||||
// Inputs should be in the form "KEY=VAL".
|
||||
func WithEnv(env []string) func(*Runner) error {
|
||||
return func(r *Runner) error {
|
||||
r.cmd.Env = append(r.cmd.Env, env...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithShell adds the -S flag to run an osqueryi shell.
|
||||
func WithShell() func(*Runner) error {
|
||||
return func(r *Runner) error {
|
||||
r.cmd.Args = append(r.cmd.Args, "-S")
|
||||
r.cmd.Stdin = os.Stdin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDataPath(path string) func(*Runner) error {
|
||||
return func(r *Runner) error {
|
||||
if err := os.MkdirAll(filepath.Join(path, "logs"), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "initialize osquery data path")
|
||||
}
|
||||
|
||||
r.cmd.Args = append(r.cmd.Args,
|
||||
"--pidfile="+filepath.Join(path, "osquery.pid"),
|
||||
"--database_path="+filepath.Join(path, "osquery.db"),
|
||||
"--extensions_socket="+filepath.Join(path, "osquery.em"),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogPath(path string) func(*Runner) error {
|
||||
return func(r *Runner) error {
|
||||
if err := os.MkdirAll(path, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "initialize osquery log path")
|
||||
}
|
||||
|
||||
r.cmd.Args = append(r.cmd.Args,
|
||||
"--logger_path="+path,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Execute begins running osqueryd and returns when the process exits. The
|
||||
// process may not be restarted after exit. Instead create a new one with
|
||||
// NewRunner.
|
||||
func (r *Runner) Execute() error {
|
||||
log.Info().Str("cmd", r.cmd.String()).Msg("run osqueryd")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
r.cancel = cancel
|
||||
|
||||
if err := r.proc.Start(); err != nil {
|
||||
return errors.Wrap(err, "start osqueryd")
|
||||
}
|
||||
|
||||
if err := r.proc.WaitOrKill(ctx, 10*time.Second); err != nil {
|
||||
return errors.Wrap(err, "osqueryd exited with error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Runner interrupts the running osquery process.
|
||||
func (r *Runner) Interrupt(err error) {
|
||||
log.Debug().Msg("interrupt osquery")
|
||||
r.cancel()
|
||||
}
|
||||
7
orbit/pkg/packaging/deb.go
Normal file
7
orbit/pkg/packaging/deb.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package packaging
|
||||
|
||||
import "github.com/goreleaser/nfpm/v2/deb"
|
||||
|
||||
func BuildDeb(opt Options) error {
|
||||
return buildNFPM(opt, deb.Default)
|
||||
}
|
||||
237
orbit/pkg/packaging/linux_shared.go
Normal file
237
orbit/pkg/packaging/linux_shared.go
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
package packaging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/update"
|
||||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func buildNFPM(opt Options, pkger nfpm.Packager) error {
|
||||
// Initialize directories
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "orbit-package")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
log.Debug().Str("path", tmpDir).Msg("created temp dir")
|
||||
|
||||
filesystemRoot := filepath.Join(tmpDir, "root")
|
||||
if err := os.MkdirAll(filesystemRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create root dir")
|
||||
}
|
||||
orbitRoot := filepath.Join(filesystemRoot, "var", "lib", "orbit")
|
||||
if err := os.MkdirAll(orbitRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create orbit dir")
|
||||
}
|
||||
|
||||
// Initialize autoupdate metadata
|
||||
|
||||
updateOpt := update.DefaultOptions
|
||||
updateOpt.Platform = "linux"
|
||||
updateOpt.RootDirectory = orbitRoot
|
||||
updateOpt.OrbitChannel = opt.OrbitChannel
|
||||
updateOpt.OsquerydChannel = opt.OsquerydChannel
|
||||
updateOpt.ServerURL = opt.UpdateURL
|
||||
if opt.UpdateRoots != "" {
|
||||
updateOpt.RootKeys = opt.UpdateRoots
|
||||
}
|
||||
|
||||
if err := initializeUpdates(updateOpt); err != nil {
|
||||
return errors.Wrap(err, "initialize updates")
|
||||
}
|
||||
|
||||
// Write files
|
||||
|
||||
if err := writeSystemdUnit(opt, filesystemRoot); err != nil {
|
||||
return errors.Wrap(err, "write systemd unit")
|
||||
}
|
||||
|
||||
if err := writeEnvFile(opt, filesystemRoot); err != nil {
|
||||
return errors.Wrap(err, "write env file")
|
||||
}
|
||||
|
||||
postInstallPath := filepath.Join(tmpDir, "postinstall.sh")
|
||||
if err := writePostInstall(opt, postInstallPath); err != nil {
|
||||
return errors.Wrap(err, "write postinstall script")
|
||||
}
|
||||
|
||||
if opt.FleetCertificate != "" {
|
||||
if err := writeCertificate(opt, orbitRoot); err != nil {
|
||||
return errors.Wrap(err, "write fleet certificate")
|
||||
}
|
||||
}
|
||||
|
||||
// Pick up all file contents
|
||||
|
||||
contents := files.Contents{
|
||||
&files.Content{
|
||||
Source: filepath.Join(filesystemRoot, "**"),
|
||||
Destination: "/",
|
||||
},
|
||||
// Symlink current into /var/lib/orbit/bin/orbit/orbit
|
||||
&files.Content{
|
||||
Source: "/var/lib/orbit/bin/orbit/linux/" + opt.OrbitChannel + "/orbit",
|
||||
Destination: "/var/lib/orbit/bin/orbit/orbit",
|
||||
Type: "symlink",
|
||||
FileInfo: &files.ContentFileInfo{
|
||||
Mode: constant.DefaultExecutableMode | os.ModeSymlink,
|
||||
},
|
||||
},
|
||||
// Symlink current into /usr/local/bin
|
||||
&files.Content{
|
||||
Source: "/var/lib/orbit/bin/orbit/orbit",
|
||||
Destination: "/usr/local/bin/orbit",
|
||||
Type: "symlink",
|
||||
FileInfo: &files.ContentFileInfo{
|
||||
Mode: constant.DefaultExecutableMode | os.ModeSymlink,
|
||||
},
|
||||
},
|
||||
}
|
||||
contents, err = files.ExpandContentGlobs(contents, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "glob contents")
|
||||
}
|
||||
for _, c := range contents {
|
||||
log.Debug().Interface("file", c).Msg("added file")
|
||||
}
|
||||
|
||||
// Build package
|
||||
|
||||
info := &nfpm.Info{
|
||||
Name: "orbit-osquery",
|
||||
Version: opt.Version,
|
||||
Description: "Orbit osquery -- runtime and autoupdater by Fleet",
|
||||
Arch: "amd64",
|
||||
Maintainer: "Fleet Engineers <engineering@fleetdm.com>",
|
||||
Homepage: "https://github.com/fleetdm/orbit",
|
||||
Overridables: nfpm.Overridables{
|
||||
Contents: contents,
|
||||
EmptyFolders: []string{
|
||||
"/var/log/osquery",
|
||||
"/var/log/orbit",
|
||||
},
|
||||
Scripts: nfpm.Scripts{
|
||||
PostInstall: postInstallPath,
|
||||
},
|
||||
},
|
||||
}
|
||||
filename := pkger.ConventionalFileName(info)
|
||||
|
||||
out, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, constant.DefaultFileMode)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open output file")
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
if err := pkger.Package(info, out); err != nil {
|
||||
return errors.Wrap(err, "write package")
|
||||
}
|
||||
log.Info().Str("path", filename).Msg("wrote package")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeSystemdUnit(opt Options, rootPath string) error {
|
||||
systemdRoot := filepath.Join(rootPath, "usr", "lib", "systemd", "system")
|
||||
if err := os.MkdirAll(systemdRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create systemd dir")
|
||||
}
|
||||
if err := ioutil.WriteFile(
|
||||
filepath.Join(systemdRoot, "orbit.service"),
|
||||
[]byte(`
|
||||
[Unit]
|
||||
Description=Orbit osquery
|
||||
After=network.service syslog.service
|
||||
StartLimitIntervalSec=0
|
||||
|
||||
[Service]
|
||||
TimeoutStartSec=0
|
||||
EnvironmentFile=/etc/default/orbit
|
||||
ExecStart=/var/lib/orbit/bin/orbit/orbit
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
KillMode=control-group
|
||||
KillSignal=SIGTERM
|
||||
CPUQuota=20%
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`),
|
||||
constant.DefaultFileMode,
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var envTemplate = template.Must(template.New("env").Parse(`
|
||||
ORBIT_UPDATE_URL={{ .UpdateURL }}
|
||||
ORBIT_ORBIT_CHANNEL={{ .OrbitChannel }}
|
||||
ORBIT_OSQUERYD_CHANNEL={{ .OsquerydChannel }}
|
||||
{{ if .Insecure }}ORBIT_INSECURE=true{{ end }}
|
||||
{{ if .FleetURL }}ORBIT_FLEET_URL={{.FleetURL}}{{ end }}
|
||||
{{ if .FleetCertificate }}ORBIT_FLEET_CERTIFICATE=/var/lib/orbit/fleet.pem{{ end }}
|
||||
{{ if .EnrollSecret }}ORBIT_ENROLL_SECRET={{.EnrollSecret}}{{ end }}
|
||||
{{ if .Debug }}ORBIT_DEBUG=true{{ end }}
|
||||
`))
|
||||
|
||||
func writeEnvFile(opt Options, rootPath string) error {
|
||||
envRoot := filepath.Join(rootPath, "etc", "default")
|
||||
if err := os.MkdirAll(envRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create env dir")
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
if err := envTemplate.Execute(&contents, opt); err != nil {
|
||||
return errors.Wrap(err, "execute template")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(
|
||||
filepath.Join(envRoot, "orbit"),
|
||||
contents.Bytes(),
|
||||
constant.DefaultFileMode,
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var postInstallTemplate = template.Must(template.New("postinstall").Parse(`
|
||||
#!/bin/sh
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# If we have a systemd, daemon-reload away now
|
||||
if [ -x /bin/systemctl ] && pidof systemd ; then
|
||||
/bin/systemctl daemon-reload 2>/dev/null 2>&1
|
||||
{{ if .StartService -}}
|
||||
/bin/systemctl restart orbit.service 2>&1
|
||||
{{- end}}
|
||||
fi
|
||||
`))
|
||||
|
||||
func writePostInstall(opt Options, path string) error {
|
||||
var contents bytes.Buffer
|
||||
if err := postInstallTemplate.Execute(&contents, opt); err != nil {
|
||||
return errors.Wrap(err, "execute template")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents.Bytes(), constant.DefaultFileMode); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
346
orbit/pkg/packaging/macos.go
Normal file
346
orbit/pkg/packaging/macos.go
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
package packaging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/update"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// See helful docs in http://bomutils.dyndns.org/tutorial.html
|
||||
|
||||
// BuildPkg builds a macOS .pkg. So far this is tested only on macOS but in theory it works with bomutils on
|
||||
// Linux.
|
||||
func BuildPkg(opt Options) error {
|
||||
// Initialize directories
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "orbit-package")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
log.Debug().Str("path", tmpDir).Msg("created temp dir")
|
||||
|
||||
filesystemRoot := filepath.Join(tmpDir, "root")
|
||||
if err := os.MkdirAll(filesystemRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create root dir")
|
||||
}
|
||||
orbitRoot := filepath.Join(filesystemRoot, "var", "lib", "orbit")
|
||||
if err := os.MkdirAll(orbitRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create orbit dir")
|
||||
}
|
||||
|
||||
// Initialize autoupdate metadata
|
||||
|
||||
updateOpt := update.DefaultOptions
|
||||
updateOpt.Platform = "macos"
|
||||
updateOpt.RootDirectory = orbitRoot
|
||||
updateOpt.OrbitChannel = opt.OrbitChannel
|
||||
updateOpt.OsquerydChannel = opt.OsquerydChannel
|
||||
updateOpt.ServerURL = opt.UpdateURL
|
||||
if opt.UpdateRoots != "" {
|
||||
updateOpt.RootKeys = opt.UpdateRoots
|
||||
}
|
||||
|
||||
if err := initializeUpdates(updateOpt); err != nil {
|
||||
return errors.Wrap(err, "initialize updates")
|
||||
}
|
||||
|
||||
// Write files
|
||||
|
||||
if err := writePackageInfo(opt, tmpDir); err != nil {
|
||||
return errors.Wrap(err, "write PackageInfo")
|
||||
}
|
||||
if err := writeDistribution(opt, tmpDir); err != nil {
|
||||
return errors.Wrap(err, "write Distribution")
|
||||
}
|
||||
if err := writeScripts(opt, tmpDir); err != nil {
|
||||
return errors.Wrap(err, "write postinstall")
|
||||
}
|
||||
if err := writeSecret(opt, orbitRoot); err != nil {
|
||||
return errors.Wrap(err, "write enroll secret")
|
||||
}
|
||||
if opt.StartService {
|
||||
if err := writeLaunchd(opt, filesystemRoot); err != nil {
|
||||
return errors.Wrap(err, "write launchd")
|
||||
}
|
||||
}
|
||||
if opt.FleetCertificate != "" {
|
||||
if err := writeCertificate(opt, orbitRoot); err != nil {
|
||||
return errors.Wrap(err, "write fleet certificate")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO gate behind a flag and allow copying a local orbit
|
||||
// if err := copyFile(
|
||||
// "./orbit",
|
||||
// filepath.Join(orbitRoot, "bin", "orbit", "macos", "current", "orbit"),
|
||||
// 0755,
|
||||
// ); err != nil {
|
||||
// return errors.Wrap(err, "write orbit")
|
||||
// }
|
||||
|
||||
// Build package
|
||||
|
||||
if err := xarBom(opt, tmpDir); err != nil {
|
||||
return errors.Wrap(err, "build pkg")
|
||||
}
|
||||
|
||||
generatedPath := filepath.Join(tmpDir, "orbit.pkg")
|
||||
|
||||
if len(opt.SignIdentity) != 0 {
|
||||
log.Info().Str("identity", opt.SignIdentity).Msg("productsign package")
|
||||
if err := signPkg(generatedPath, opt.SignIdentity); err != nil {
|
||||
return errors.Wrap(err, "productsign")
|
||||
}
|
||||
}
|
||||
|
||||
if opt.Notarize {
|
||||
if err := notarizePkg(generatedPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("orbit-osquery_%s_amd64.pkg", opt.Version)
|
||||
if err := os.Rename(generatedPath, filename); err != nil {
|
||||
return errors.Wrap(err, "rename pkg")
|
||||
}
|
||||
log.Info().Str("path", filename).Msg("wrote pkg package")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writePackageInfo(opt Options, rootPath string) error {
|
||||
// PackageInfo is metadata for the pkg
|
||||
path := filepath.Join(rootPath, "flat", "base.pkg", "PackageInfo")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
if err := macosPackageInfoTemplate.Execute(&contents, opt); err != nil {
|
||||
return errors.Wrap(err, "execute template")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents.Bytes(), constant.DefaultFileMode); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeScripts(opt Options, rootPath string) error {
|
||||
// Postinstall script
|
||||
path := filepath.Join(rootPath, "scripts", "postinstall")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
if err := macosPostinstallTemplate.Execute(&contents, opt); err != nil {
|
||||
return errors.Wrap(err, "execute template")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents.Bytes(), 0744); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeLaunchd(opt Options, rootPath string) error {
|
||||
// launchd is the service mechanism on macOS
|
||||
path := filepath.Join(rootPath, "Library", "LaunchDaemons", "com.fleetdm.orbit.plist")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
if err := macosLaunchdTemplate.Execute(&contents, opt); err != nil {
|
||||
return errors.Wrap(err, "execute template")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents.Bytes(), 0644); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeDistribution(opt Options, rootPath string) error {
|
||||
// Distribution file is metadata for the pkg
|
||||
path := filepath.Join(rootPath, "flat", "Distribution")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
if err := macosDistributionTemplate.Execute(&contents, opt); err != nil {
|
||||
return errors.Wrap(err, "execute template")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents.Bytes(), constant.DefaultFileMode); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeCertificate(opt Options, orbitRoot string) error {
|
||||
// Fleet TLS certificate
|
||||
dstPath := filepath.Join(orbitRoot, "fleet.pem")
|
||||
|
||||
if err := copyFile(opt.FleetCertificate, dstPath, 0644); err != nil {
|
||||
return errors.Wrap(err, "write orbit")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// xarBom creates the actual .pkg format. It's a xar archive with a BOM (Bill of
|
||||
// materials?). See http://bomutils.dyndns.org/tutorial.html.
|
||||
func xarBom(opt Options, rootPath string) error {
|
||||
// Adapted from BSD licensed
|
||||
// https://github.com/go-flutter-desktop/hover/blob/v0.46.2/cmd/packaging/darwin-pkg.go
|
||||
|
||||
// Copy payload/scripts
|
||||
if err := cpio(
|
||||
filepath.Join(rootPath, "root"),
|
||||
filepath.Join(rootPath, "flat", "base.pkg", "Payload"),
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "cpio Payload")
|
||||
}
|
||||
if err := cpio(
|
||||
filepath.Join(rootPath, "scripts"),
|
||||
filepath.Join(rootPath, "flat", "base.pkg", "Scripts"),
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "cpio Scripts")
|
||||
}
|
||||
|
||||
// Make bom
|
||||
var cmdMkbom *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
cmdMkbom = exec.Command("mkbom", filepath.Join(rootPath, "root"), filepath.Join("flat", "base.pkg", "Bom"))
|
||||
case "linux":
|
||||
cmdMkbom = exec.Command("mkbom", "-u", "0", "-g", "80", filepath.Join(rootPath, "flat", "root"), filepath.Join("flat", "base.pkg", "Bom"))
|
||||
}
|
||||
cmdMkbom.Dir = rootPath
|
||||
cmdMkbom.Stdout = os.Stdout
|
||||
cmdMkbom.Stderr = os.Stderr
|
||||
if err := cmdMkbom.Run(); err != nil {
|
||||
return errors.Wrap(err, "mkbom")
|
||||
}
|
||||
|
||||
// List files for xar
|
||||
var files []string
|
||||
err := filepath.Walk(
|
||||
filepath.Join(rootPath, "flat"),
|
||||
func(path string, info os.FileInfo, _ error) error {
|
||||
relativePath, err := filepath.Rel(filepath.Join(rootPath, "flat"), path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files = append(files, relativePath)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "iterate files")
|
||||
}
|
||||
|
||||
// Make xar
|
||||
cmdXar := exec.Command("xar", append([]string{"--compression", "none", "-cf", filepath.Join("..", "orbit.pkg")}, files...)...)
|
||||
cmdXar.Dir = filepath.Join(rootPath, "flat")
|
||||
cmdXar.Stdout = os.Stdout
|
||||
cmdXar.Stderr = os.Stderr
|
||||
|
||||
if err := cmdXar.Run(); err != nil {
|
||||
return errors.Wrap(err, "run xar")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cpio(srcPath, dstPath string) error {
|
||||
// This is the compression routine that is expected for pkg files.
|
||||
dst, err := os.OpenFile(dstPath, os.O_RDWR|os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open dst")
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
cmdFind := exec.Command("find", ".")
|
||||
cmdFind.Dir = srcPath
|
||||
cmdCpio := exec.Command("cpio", "-o", "--format", "odc", "-R", "0:80")
|
||||
cmdCpio.Dir = srcPath
|
||||
cmdGzip := exec.Command("gzip", "-c")
|
||||
|
||||
// Pipes like this: find | cpio | gzip > dstPath
|
||||
cmdCpio.Stdin, err = cmdFind.StdoutPipe()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "pipe cpio")
|
||||
}
|
||||
cmdGzip.Stdin, err = cmdCpio.StdoutPipe()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "pipe gzip")
|
||||
}
|
||||
cmdGzip.Stdout = dst
|
||||
|
||||
err = cmdGzip.Start()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "start gzip")
|
||||
}
|
||||
err = cmdCpio.Start()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "start cpio")
|
||||
}
|
||||
err = cmdFind.Run()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "run find")
|
||||
}
|
||||
err = cmdCpio.Wait()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "wait cpio")
|
||||
}
|
||||
err = cmdGzip.Wait()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "wait gzip")
|
||||
}
|
||||
err = dst.Close()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "close dst")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func signPkg(pkgPath, identity string) error {
|
||||
var outBuf bytes.Buffer
|
||||
cmdProductsign := exec.Command(
|
||||
"productsign",
|
||||
"--sign", identity,
|
||||
pkgPath,
|
||||
pkgPath+".signed",
|
||||
)
|
||||
cmdProductsign.Stdout = &outBuf
|
||||
cmdProductsign.Stderr = &outBuf
|
||||
if err := cmdProductsign.Run(); err != nil {
|
||||
fmt.Println(outBuf.String())
|
||||
return errors.Wrap(err, "productsign")
|
||||
}
|
||||
|
||||
if err := os.Rename(pkgPath+".signed", pkgPath); err != nil {
|
||||
return errors.Wrap(err, "rename signed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
88
orbit/pkg/packaging/macos_notarize.go
Normal file
88
orbit/pkg/packaging/macos_notarize.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package packaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/mitchellh/gon/notarize"
|
||||
"github.com/mitchellh/gon/staple"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func notarizePkg(pkgPath string) error {
|
||||
username, ok := os.LookupEnv("AC_USERNAME")
|
||||
if !ok {
|
||||
return errors.New("AC_USERNAME must be set in environment")
|
||||
}
|
||||
|
||||
password, ok := os.LookupEnv("AC_PASSWORD")
|
||||
if !ok {
|
||||
return errors.New("AC_PASSWORD must be set in environment")
|
||||
}
|
||||
|
||||
info, err := notarize.Notarize(
|
||||
context.Background(),
|
||||
¬arize.Options{
|
||||
File: pkgPath,
|
||||
BundleId: "com.fleetdm.orbit",
|
||||
Username: username,
|
||||
Password: password,
|
||||
Status: &statusHuman{
|
||||
Lock: &sync.Mutex{},
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "notarize")
|
||||
}
|
||||
|
||||
log.Info().Str("logs", info.LogFileURL).Msg("notarization completed")
|
||||
|
||||
if err := staple.Staple(context.Background(), &staple.Options{File: pkgPath}); err != nil {
|
||||
return errors.Wrap(err, "staple notarization")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This status plugin copied from
|
||||
// https://github.com/mitchellh/gon/blob/v0.2.3/cmd/gon/status_human.go since it
|
||||
// is not exposed from the gon library.
|
||||
|
||||
// statusHuman implements notarize.Status and outputs information to
|
||||
// the CLI for human consumption.
|
||||
type statusHuman struct {
|
||||
Prefix string
|
||||
Lock *sync.Mutex
|
||||
|
||||
lastStatus string
|
||||
}
|
||||
|
||||
func (s *statusHuman) Submitting() {
|
||||
s.Lock.Lock()
|
||||
defer s.Lock.Unlock()
|
||||
|
||||
color.New().Fprintf(os.Stdout, " %sSubmitting file for notarization...\n", s.Prefix)
|
||||
}
|
||||
|
||||
func (s *statusHuman) Submitted(uuid string) {
|
||||
s.Lock.Lock()
|
||||
defer s.Lock.Unlock()
|
||||
|
||||
color.New().Fprintf(os.Stdout, " %sSubmitted. Request UUID: %s\n", s.Prefix, uuid)
|
||||
color.New().Fprintf(
|
||||
os.Stdout, " %sWaiting for results from Apple. This can take minutes to hours.\n", s.Prefix)
|
||||
}
|
||||
|
||||
func (s *statusHuman) Status(info notarize.Info) {
|
||||
s.Lock.Lock()
|
||||
defer s.Lock.Unlock()
|
||||
|
||||
if info.Status != s.lastStatus {
|
||||
s.lastStatus = info.Status
|
||||
color.New().Fprintf(os.Stdout, " %sStatus: %s\n", s.Prefix, info.Status)
|
||||
}
|
||||
}
|
||||
83
orbit/pkg/packaging/macos_templates.go
Normal file
83
orbit/pkg/packaging/macos_templates.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package packaging
|
||||
|
||||
import "text/template"
|
||||
|
||||
// Best reference I could find:
|
||||
// http://s.sudre.free.fr/Stuff/Ivanhoe/FLAT.html
|
||||
var macosPackageInfoTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
|
||||
`<pkg-info format-version="2" identifier="{{.Identifier}}.base.pkg" version="{{.Version}}" install-location="/" auth="root">
|
||||
<scripts>
|
||||
<postinstall file="./postinstall"/>
|
||||
</scripts>
|
||||
<bundle-version>
|
||||
</bundle-version>
|
||||
</pkg-info>
|
||||
`))
|
||||
|
||||
// Reference:
|
||||
// https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html
|
||||
var macosDistributionTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
|
||||
`<?xml version="1.0" encoding="utf-8"?>
|
||||
<installer-gui-script minSpecVersion="2">
|
||||
<title>Orbit</title>
|
||||
<choices-outline>
|
||||
<line choice="choiceBase"/>
|
||||
</choices-outline>
|
||||
<choice id="choiceBase" title="Orbit osquery" enabled="false" selected="true" description="Standard installation for Orbit osquery.">
|
||||
<pkg-ref id="{{.Identifier}}.base.pkg"/>
|
||||
</choice>
|
||||
<pkg-ref id="{{.Identifier}}.base.pkg" version="{{.Version}}" auth="root">#base.pkg</pkg-ref>
|
||||
</installer-gui-script>
|
||||
`))
|
||||
|
||||
var macosPostinstallTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
|
||||
`#!/bin/bash
|
||||
|
||||
ln -sf /var/lib/orbit/bin/orbit/macos/{{.OrbitChannel}}/orbit /var/lib/orbit/bin/orbit/orbit
|
||||
ln -sf /var/lib/orbit/bin/orbit/orbit /usr/local/bin/orbit
|
||||
|
||||
{{ if .StartService -}}
|
||||
launchctl unload /Library/LaunchDaemons/com.fleetdm.orbit.plist
|
||||
launchctl load -w /Library/LaunchDaemons/com.fleetdm.orbit.plist
|
||||
{{- end }}
|
||||
`))
|
||||
|
||||
// TODO set Nice?
|
||||
//
|
||||
//Note it's important not to start the orbit binary in
|
||||
// `/usr/local/bin/orbit` because this is a path that users usually have write
|
||||
// access to, and running that binary with launchd can become a privilege
|
||||
// escalation vector.
|
||||
var macosLaunchdTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
|
||||
`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.fleetdm.orbit</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/var/lib/orbit/bin/orbit/orbit</string>
|
||||
</array>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/orbit/orbit.stdout.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/orbit/orbit.stderr.log</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>ORBIT_UPDATE_URL</key><string>{{ .UpdateURL }}</string>
|
||||
<key>ORBIT_ORBIT_CHANNEL</key><string>{{ .OrbitChannel }}</string>
|
||||
<key>ORBIT_OSQUERYD_CHANNEL</key><string>{{ .OsquerydChannel }}</string>
|
||||
{{ if .Insecure }}<key>ORBIT_INSECURE</key><string>true</string>{{ end }}
|
||||
{{ if .FleetURL }}<key>ORBIT_FLEET_URL</key><string>{{ .FleetURL }}</string>{{ end }}
|
||||
{{ if .FleetCertificate }}<key>ORBIT_FLEET_CERTIFICATE</key><string>/var/lib/orbit/fleet.pem</string>{{ end }}
|
||||
{{ if .EnrollSecret }}<key>ORBIT_ENROLL_SECRET_PATH</key><string>/var/lib/orbit/secret.txt</string>{{ end }}
|
||||
{{ if .Debug }}<key>ORBIT_DEBUG</key><string>true</string>{{ end }}
|
||||
</dict>
|
||||
<key>KeepAlive</key><true/>
|
||||
<key>RunAtLoad</key><true/>
|
||||
<key>ThrottleInterval</key>
|
||||
<integer>10</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
`))
|
||||
131
orbit/pkg/packaging/packaging.go
Normal file
131
orbit/pkg/packaging/packaging.go
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// package packaging provides tools for buildin Orbit installation packages.
|
||||
package packaging
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/update"
|
||||
"github.com/fleetdm/orbit/pkg/update/filestore"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Options are the configurable options provided for the package.
|
||||
type Options struct {
|
||||
// FleetURL is the URL to the Fleet server.
|
||||
FleetURL string
|
||||
// EnrollSecret is the enroll secret used to authenticate to the Fleet
|
||||
// server.
|
||||
EnrollSecret string
|
||||
// Version is the version number for this package.
|
||||
Version string
|
||||
// Identifier is the identifier (eg. com.fleetdm.orbit) for the package product.
|
||||
Identifier string
|
||||
// StartService is a boolean indicating whether to start a system-specific
|
||||
// background service.
|
||||
StartService bool
|
||||
// Insecure enables insecure TLS connections for the generated package.
|
||||
Insecure bool
|
||||
// SignIdentity is the codesigning identity to use (only macOS at this time)
|
||||
SignIdentity string
|
||||
// Notarize sets whether macOS packages should be Notarized.
|
||||
Notarize bool
|
||||
// FleetCertificate is a path to a server certificate to include in the package.
|
||||
FleetCertificate string
|
||||
// OrbitChannel is the update channel to use for Orbit.
|
||||
OrbitChannel string
|
||||
// OsquerydChannel is the update channel to use for Osquery (osqueryd).
|
||||
OsquerydChannel string
|
||||
// UpdateURL is the base URL of the update server (TUF repository).
|
||||
UpdateURL string
|
||||
// UpdateRoots is the root JSON metadata for update server (TUF repository).
|
||||
UpdateRoots string
|
||||
// Debug determines whether to enable debug logging for the agent.
|
||||
Debug bool
|
||||
}
|
||||
|
||||
func copyFile(srcPath, dstPath string, perm os.FileMode) error {
|
||||
src, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open src for copy")
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
|
||||
return errors.Wrap(err, "create dst dir for copy")
|
||||
}
|
||||
|
||||
dst, err := os.OpenFile(dstPath, os.O_RDWR|os.O_CREATE, perm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open dst for copy")
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if _, err := io.Copy(dst, src); err != nil {
|
||||
return errors.Wrap(err, "copy src to dst")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initializeUpdates(updateOpt update.Options) error {
|
||||
localStore, err := filestore.New(filepath.Join(updateOpt.RootDirectory, "tuf-metadata.json"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create local metadata store")
|
||||
}
|
||||
updateOpt.LocalStore = localStore
|
||||
|
||||
updater, err := update.New(updateOpt)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to init updater")
|
||||
}
|
||||
if err := updater.UpdateMetadata(); err != nil {
|
||||
return errors.Wrap(err, "failed to update metadata")
|
||||
}
|
||||
osquerydPath, err := updater.Get("osqueryd", updateOpt.OsquerydChannel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get osqueryd")
|
||||
}
|
||||
log.Debug().Str("path", osquerydPath).Msg("got osqueryd")
|
||||
|
||||
orbitPath, err := updater.Get("orbit", updateOpt.OrbitChannel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get orbit")
|
||||
}
|
||||
log.Debug().Str("path", orbitPath).Msg("got orbit")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeSecret(opt Options, orbitRoot string) error {
|
||||
// Enroll secret
|
||||
path := filepath.Join(orbitRoot, "secret.txt")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, []byte(opt.EnrollSecret), 0600); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func chmodRecursive(path string, perm os.FileMode) error {
|
||||
return filepath.Walk(path, func(path string, info fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "walk error")
|
||||
}
|
||||
|
||||
if err := os.Chmod(path, perm); err != nil {
|
||||
return errors.Wrap(err, "chmod")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
7
orbit/pkg/packaging/rpm.go
Normal file
7
orbit/pkg/packaging/rpm.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package packaging
|
||||
|
||||
import "github.com/goreleaser/nfpm/v2/rpm"
|
||||
|
||||
func BuildRPM(opt Options) error {
|
||||
return buildNFPM(opt, rpm.Default)
|
||||
}
|
||||
109
orbit/pkg/packaging/windows.go
Normal file
109
orbit/pkg/packaging/windows.go
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
package packaging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/packaging/wix"
|
||||
"github.com/fleetdm/orbit/pkg/update"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// BuildMSI builds a Windows .msi.
|
||||
func BuildMSI(opt Options) error {
|
||||
// Initialize directories
|
||||
tmpDir, err := ioutil.TempDir("", "orbit-package")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
log.Debug().Str("path", tmpDir).Msg("created temp dir")
|
||||
|
||||
filesystemRoot := filepath.Join(tmpDir, "root")
|
||||
if err := os.MkdirAll(filesystemRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create root dir")
|
||||
}
|
||||
orbitRoot := filesystemRoot
|
||||
if err := os.MkdirAll(orbitRoot, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "create orbit dir")
|
||||
}
|
||||
|
||||
// Initialize autoupdate metadata
|
||||
|
||||
updateOpt := update.DefaultOptions
|
||||
updateOpt.Platform = "windows"
|
||||
updateOpt.RootDirectory = orbitRoot
|
||||
updateOpt.OrbitChannel = opt.OrbitChannel
|
||||
updateOpt.OsquerydChannel = opt.OsquerydChannel
|
||||
updateOpt.ServerURL = opt.UpdateURL
|
||||
if opt.UpdateRoots != "" {
|
||||
updateOpt.RootKeys = opt.UpdateRoots
|
||||
}
|
||||
|
||||
if err := initializeUpdates(updateOpt); err != nil {
|
||||
return errors.Wrap(err, "initialize updates")
|
||||
}
|
||||
|
||||
// Write files
|
||||
|
||||
if err := writeSecret(opt, orbitRoot); err != nil {
|
||||
return errors.Wrap(err, "write enroll secret")
|
||||
}
|
||||
|
||||
if err := writeWixFile(opt, tmpDir); err != nil {
|
||||
return errors.Wrap(err, "write wix file")
|
||||
}
|
||||
|
||||
// Make sure permissions are permissive so that the `wine` user in the Wix Docker container can access files.
|
||||
if err := chmodRecursive(tmpDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := wix.Heat(tmpDir); err != nil {
|
||||
return errors.Wrap(err, "package root files")
|
||||
}
|
||||
|
||||
if err := wix.TransformHeat(filepath.Join(tmpDir, "heat.wxs")); err != nil {
|
||||
return errors.Wrap(err, "transform heat")
|
||||
}
|
||||
|
||||
if err := wix.Candle(tmpDir); err != nil {
|
||||
return errors.Wrap(err, "build package")
|
||||
}
|
||||
|
||||
if err := wix.Light(tmpDir); err != nil {
|
||||
return errors.Wrap(err, "build package")
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("orbit-osquery_%s.msi", opt.Version)
|
||||
if err := os.Rename(filepath.Join(tmpDir, "orbit.msi"), filename); err != nil {
|
||||
return errors.Wrap(err, "rename msi")
|
||||
}
|
||||
log.Info().Str("path", filename).Msg("wrote msi package")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeWixFile(opt Options, rootPath string) error {
|
||||
// PackageInfo is metadata for the pkg
|
||||
path := filepath.Join(rootPath, "main.wxs")
|
||||
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
if err := windowsWixTemplate.Execute(&contents, opt); err != nil {
|
||||
return errors.Wrap(err, "execute template")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents.Bytes(), 0o666); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
87
orbit/pkg/packaging/windows_templates.go
Normal file
87
orbit/pkg/packaging/windows_templates.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package packaging
|
||||
|
||||
import "text/template"
|
||||
|
||||
// Partially adapted from Launcher's wix XML in
|
||||
// https://github.com/kolide/launcher/blob/master/pkg/packagekit/internal/assets/main.wxs.
|
||||
var windowsWixTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
|
||||
`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||
<Product
|
||||
Id="C2C2437D-0562-465E-A0BB-2C4484025BD6"
|
||||
Name="Orbit osquery"
|
||||
Language="1033"
|
||||
Version="{{.Version}}"
|
||||
Manufacturer="Fleet Device Management (fleetdm.com)"
|
||||
UpgradeCode="B681CB20-107E-428A-9B14-2D3C1AFED244" >
|
||||
|
||||
<Package
|
||||
Id="*"
|
||||
Keywords='orbit osquery'
|
||||
Description="Orbit osquery"
|
||||
InstallerVersion="500"
|
||||
Compressed="yes"
|
||||
InstallScope="perMachine"
|
||||
InstallPrivileges="elevated"
|
||||
Languages="1033" />
|
||||
|
||||
<MediaTemplate EmbedCab="yes" />
|
||||
|
||||
<MajorUpgrade AllowDowngrades="yes" />
|
||||
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
<Directory Id="ORBITROOT" Name="Orbit">
|
||||
<Component Id="C_ORBITROOT" Guid="A7DFD09E-2D2B-4535-A04F-5D4DE90F3863">
|
||||
<CreateFolder>
|
||||
<PermissionEx Sddl="O:SYG:SYD:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)" />
|
||||
</CreateFolder>
|
||||
</Component>
|
||||
<Directory Id="ORBITBIN" Name="bin">
|
||||
<Directory Id="ORBITBINORBIT" Name="orbit">
|
||||
<Component Id="C_ORBITBIN" Guid="AF347B4E-B84B-4DD4-9C4D-133BE17B613D">
|
||||
<CreateFolder>
|
||||
<PermissionEx Sddl="O:SYG:SYD:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)" />
|
||||
</CreateFolder>
|
||||
<File Source="root\bin\orbit\windows\stable\orbit.exe">
|
||||
<PermissionEx Sddl="O:SYG:SYD:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)" />
|
||||
</File>
|
||||
<ServiceInstall
|
||||
Name="Orbit osquery"
|
||||
Account="NT AUTHORITY\SYSTEM"
|
||||
ErrorControl="ignore"
|
||||
Start="auto"
|
||||
Type="ownProcess"
|
||||
Arguments='--root-dir "[ORBITROOT]." --log-file "[ORBITROOT]orbit-log.txt" {{ if .FleetURL }}--fleet-url "{{ .FleetURL }}"{{ end }} {{ if .EnrollSecret }}--enroll-secret-path "[ORBITROOT]secret.txt"{{ end }} {{if .Insecure }}--insecure{{ end }} {{ if .UpdateURL }}--update-url "{{ .UpdateURL }}" {{ end }} --orbit-channel "{{ .OrbitChannel }}" --osqueryd-channel "{{ .OsquerydChannel }}"'
|
||||
>
|
||||
<util:ServiceConfig
|
||||
FirstFailureActionType="restart"
|
||||
SecondFailureActionType="restart"
|
||||
ThirdFailureActionType="restart"
|
||||
ResetPeriodInDays="1"
|
||||
RestartServiceDelayInSeconds="1"
|
||||
/>
|
||||
</ServiceInstall>
|
||||
<ServiceControl
|
||||
Id="StartOrbitService"
|
||||
Name="Orbit osquery"
|
||||
Start="install"
|
||||
Stop="both"
|
||||
Remove="uninstall"
|
||||
/>
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
<Feature Id="Orbit" Title="Orbit osquery" Level="1" Display="hidden">
|
||||
<ComponentGroupRef Id="OrbitFiles" />
|
||||
<ComponentRef Id="C_ORBITBIN" />
|
||||
<ComponentRef Id="C_ORBITROOT" />
|
||||
</Feature>
|
||||
|
||||
</Product>
|
||||
</Wix>
|
||||
`))
|
||||
125
orbit/pkg/packaging/wix/transform.go
Normal file
125
orbit/pkg/packaging/wix/transform.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
package wix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
XMLName xml.Name
|
||||
Attrs attrs `xml:",any,attr"`
|
||||
Content string `xml:",chardata"`
|
||||
Children []*node `xml:",any"`
|
||||
}
|
||||
|
||||
type attrs []*xml.Attr
|
||||
|
||||
// Get the value of the attr with the provided name, otherwise returning an
|
||||
// empty string.
|
||||
func (a attrs) Get(name string) string {
|
||||
for _, attr := range a {
|
||||
if attr.Name.Local == name {
|
||||
return attr.Value
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func xmlAttr(name, value string) *xml.Attr {
|
||||
return &xml.Attr{Name: xml.Name{Local: name}, Value: value}
|
||||
}
|
||||
|
||||
func xmlNode(name string, attrs ...*xml.Attr) *node {
|
||||
return &node{
|
||||
XMLName: xml.Name{Local: name},
|
||||
Attrs: attrs,
|
||||
}
|
||||
}
|
||||
|
||||
func TransformHeat(path string) error {
|
||||
contents, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read file")
|
||||
}
|
||||
|
||||
// Eliminate line feeds (they cause extra junk in the result)
|
||||
contents = bytes.ReplaceAll(contents, []byte("\r"), []byte(""))
|
||||
|
||||
var n node
|
||||
if err := xml.Unmarshal(contents, &n); err != nil {
|
||||
return errors.Wrap(err, "unmarshal xml")
|
||||
}
|
||||
|
||||
stack := []*node{}
|
||||
if err := transform(&n, &stack); err != nil {
|
||||
return errors.Wrap(err, "in transform")
|
||||
}
|
||||
|
||||
contents, err = xml.MarshalIndent(n, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal xml")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, contents, 0o600); err != nil {
|
||||
return errors.Wrap(err, "write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func transform(cur *node, stack *[]*node) error {
|
||||
// Clear namespace on all elements (generates unnecessarily noisy output if
|
||||
// this is not done).
|
||||
cur.XMLName.Space = ""
|
||||
|
||||
// Change permissions for all files
|
||||
if cur.XMLName.Local == "File" {
|
||||
// This SDDL copied directly from osqueryd.exe after a regular
|
||||
// osquery MSI install. We assume that osquery is getting the
|
||||
// permissions correct and use exactly the same for our files.
|
||||
// Using this cryptic string seems to be the only way to disable
|
||||
// permission inheritance in a WiX package, so we may not have
|
||||
// any option for something more readable.
|
||||
//
|
||||
// Permissions:
|
||||
// Disable inheritance
|
||||
// SYSTEM: read/write/execute
|
||||
// Administrators: read/write/execute
|
||||
// Users: read/execute
|
||||
sddl := "O:SYG:SYD:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)"
|
||||
if strings.HasSuffix(cur.Attrs.Get("Source"), "secret.txt") {
|
||||
// This SDDL copied from properly configured file on a Windows 10
|
||||
// machine. Permissions are same as above but with access removed
|
||||
// for regular users.
|
||||
//
|
||||
// Permissions:
|
||||
// Disable inheritance
|
||||
// SYSTEM: read/write/execute
|
||||
// Administrators: read/write/execute
|
||||
sddl = "O:SYG:SYD:PAI(A;;FA;;;SY)(A;;FA;;;BA)"
|
||||
|
||||
}
|
||||
cur.Children = append(cur.Children, xmlNode(
|
||||
"PermissionEx",
|
||||
xmlAttr("Sddl", sddl),
|
||||
))
|
||||
}
|
||||
|
||||
// push current node onto stack
|
||||
*stack = append(*stack, cur)
|
||||
// Recursively walk the children
|
||||
for _, child := range cur.Children {
|
||||
if err := transform(child, stack); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// pop current node from stack
|
||||
*stack = (*stack)[:len(*stack)-1]
|
||||
|
||||
return nil
|
||||
}
|
||||
88
orbit/pkg/packaging/wix/wix.go
Normal file
88
orbit/pkg/packaging/wix/wix.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// Package wix runs the WiX packaging tools via Docker.
|
||||
//
|
||||
// WiX's documentation is available at https://wixtoolset.org/.
|
||||
package wix
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
directoryReference = "ORBITROOT"
|
||||
)
|
||||
|
||||
// Heat runs the WiX Heat command on the provided directory.
|
||||
//
|
||||
// The Heat command creates XML fragments allowing WiX to include the entire
|
||||
// directory. See
|
||||
// https://wixtoolset.org/documentation/manual/v3/overview/heat.html.
|
||||
func Heat(path string) error {
|
||||
cmd := exec.Command(
|
||||
"docker", "run", "--rm", "--platform", "linux/386",
|
||||
"--volume", path+":/wix", // mount volume
|
||||
"dactiv/wix:latest", // image name
|
||||
"heat", "dir", "root", // command in image
|
||||
"-out", "heat.wxs",
|
||||
"-gg", "-g1", // generate UUIDs (required by wix)
|
||||
"-cg", "OrbitFiles", // set ComponentGroup name
|
||||
"-scom", "-sfrag", "-srd", "-sreg", // suppress unneccesary generated items
|
||||
"-dr", directoryReference, // set reference name
|
||||
"-ke", // keep empty directories
|
||||
)
|
||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.Wrap(err, "heat failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Candle runs the WiX Candle command on the provided directory.
|
||||
//
|
||||
// See
|
||||
// https://wixtoolset.org/documentation/manual/v3/overview/candle.html.
|
||||
func Candle(path string) error {
|
||||
cmd := exec.Command(
|
||||
"docker", "run", "--rm", "--platform", "linux/386",
|
||||
"--volume", path+":/wix", // mount volume
|
||||
"dactiv/wix:latest", // image name
|
||||
"candle", "heat.wxs", "main.wxs", // command in image
|
||||
"-ext", "WixUtilExtension",
|
||||
"-arch", "x64",
|
||||
)
|
||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.Wrap(err, "candle failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Light runs the WiX Light command on the provided directory.
|
||||
//
|
||||
// See
|
||||
// https://wixtoolset.org/documentation/manual/v3/overview/light.html.
|
||||
func Light(path string) error {
|
||||
cmd := exec.Command(
|
||||
"docker", "run", "--rm", "--platform", "linux/386",
|
||||
"--volume", path+":/wix", // mount volume
|
||||
"dactiv/wix:latest", // image name
|
||||
"light", "heat.wixobj", "main.wixobj", // command in image
|
||||
"-ext", "WixUtilExtension",
|
||||
"-b", "root", // Set directory for finding heat files
|
||||
"-out", "orbit.msi",
|
||||
"-sval", // skip validation (otherwise Wine crashes)
|
||||
)
|
||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.Wrap(err, "light failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
28
orbit/pkg/platform/platform_notwindows.go
Normal file
28
orbit/pkg/platform/platform_notwindows.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//+build !windows
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ChmodExecutableDirectory sets the appropriate permissions on an executable
|
||||
// file. On POSIX this is a normal chmod call.
|
||||
func ChmodExecutableDirectory(path string) error {
|
||||
if err := os.Chmod(path, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "chmod executable directory")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChmodExecutable sets the appropriate permissions on the parent directory of
|
||||
// an executable file. On POSIX this is a regular chmod call.
|
||||
func ChmodExecutable(path string) error {
|
||||
if err := os.Chmod(path, constant.DefaultExecutableMode); err != nil {
|
||||
return errors.Wrap(err, "chmod executable")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
50
orbit/pkg/platform/platform_windows.go
Normal file
50
orbit/pkg/platform/platform_windows.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//+build windows
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/hectane/go-acl"
|
||||
)
|
||||
|
||||
const (
|
||||
fullControl = uint32(2032127)
|
||||
readAndExecute = uint32(131241)
|
||||
)
|
||||
|
||||
// ChmodExecutableDirectory sets the appropriate permissions on the parent
|
||||
// directory of an executable file. On Windows this involves setting the
|
||||
// appropriate ACLs.
|
||||
func ChmodExecutableDirectory(path string) error {
|
||||
if err := acl.Apply(
|
||||
path,
|
||||
true,
|
||||
false,
|
||||
acl.GrantSid(fullControl, constant.SystemSID),
|
||||
acl.GrantSid(fullControl, constant.AdminSID),
|
||||
acl.GrantSid(readAndExecute, constant.UserSID),
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "apply ACLs")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChmodExecutable sets the appropriate permissions on an executable file. On
|
||||
// Windows this involves setting the appropriate ACLs.
|
||||
func ChmodExecutable(path string) error {
|
||||
if err := acl.Apply(
|
||||
path,
|
||||
true,
|
||||
false,
|
||||
acl.GrantSid(fullControl, constant.SystemSID),
|
||||
acl.GrantSid(fullControl, constant.AdminSID),
|
||||
acl.GrantSid(readAndExecute, constant.UserSID),
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "apply ACLs")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
115
orbit/pkg/process/process.go
Normal file
115
orbit/pkg/process/process.go
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ExecCmd interface {
|
||||
Start() error
|
||||
Wait() error
|
||||
OsProcess() OsProcess
|
||||
}
|
||||
|
||||
type OsProcess interface {
|
||||
Signal(os.Signal) error
|
||||
Kill() error
|
||||
}
|
||||
|
||||
type execCmdWrapper struct {
|
||||
*exec.Cmd
|
||||
}
|
||||
|
||||
func (e *execCmdWrapper) OsProcess() OsProcess {
|
||||
return e.Process
|
||||
}
|
||||
|
||||
type Process struct {
|
||||
ExecCmd
|
||||
}
|
||||
|
||||
func NewWithCmd(cmd *exec.Cmd) *Process {
|
||||
return &Process{ExecCmd: &execCmdWrapper{Cmd: cmd}}
|
||||
}
|
||||
|
||||
func newWithMock(cmd ExecCmd) *Process {
|
||||
return &Process{ExecCmd: cmd}
|
||||
}
|
||||
|
||||
// WaitOrKill waits for the already-started process by calling its Wait method.
|
||||
//
|
||||
// If the process does not return before ctx is done, WaitOrKill sends it the
|
||||
// given interrupt signal. If killDelay is positive, WaitOrKill waits that
|
||||
// additional period for Wait to return before sending os.Kill.
|
||||
//
|
||||
// Adapted from Go core:
|
||||
// https://github.com/golang/go/blob/8981092d71aee273d27b0e11cf932a34d4d365c1/src/cmd/go/script_test.go#L1131-L1190
|
||||
func (p *Process) WaitOrKill(ctx context.Context, killDelay time.Duration) error {
|
||||
if p.OsProcess() == nil {
|
||||
return fmt.Errorf("WaitOrKill requires a non-nil OsProcess - missing Start call?")
|
||||
}
|
||||
|
||||
errc := make(chan error)
|
||||
go func() {
|
||||
select {
|
||||
case errc <- nil:
|
||||
return
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
err := p.OsProcess().Signal(stopSignal())
|
||||
if err == nil {
|
||||
err = ctx.Err() // Report ctx.Err() as the reason we interrupted.
|
||||
} else if err.Error() == "os: process already finished" {
|
||||
errc <- nil
|
||||
return
|
||||
}
|
||||
|
||||
if killDelay > 0 {
|
||||
timer := time.NewTimer(killDelay)
|
||||
select {
|
||||
// Report ctx.Err() as the reason we interrupted the process...
|
||||
case errc <- ctx.Err():
|
||||
timer.Stop()
|
||||
return
|
||||
// ...but after killDelay has elapsed, fall back to a stronger signal.
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
// Wait still hasn't returned.
|
||||
// Kill the process harder to make sure that it exits.
|
||||
//
|
||||
// Ignore any error: if cmd.Process has already terminated, we still
|
||||
// want to send ctx.Err() (or the error from the Interrupt call)
|
||||
// to properly attribute the signal that may have terminated it.
|
||||
_ = p.OsProcess().Kill()
|
||||
}
|
||||
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
waitErr := p.Wait()
|
||||
if interruptErr := <-errc; interruptErr != nil {
|
||||
return interruptErr
|
||||
}
|
||||
return waitErr
|
||||
}
|
||||
|
||||
// stopSignal returns the appropriate signal to use to request that a process
|
||||
// stop execution.
|
||||
//
|
||||
// Copied from Go core:
|
||||
// https://github.com/golang/go/blob/8981092d71aee273d27b0e11cf932a34d4d365c1/src/cmd/go/script_test.go#L1119-L1129
|
||||
func stopSignal() os.Signal {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Per https://golang.org/pkg/os/#Signal, “Interrupt is not implemented on
|
||||
// Windows; using it with os.Process.Signal will return an error.”
|
||||
// Fall back to Kill instead.
|
||||
return os.Kill
|
||||
}
|
||||
return os.Interrupt
|
||||
}
|
||||
62
orbit/pkg/process/process_mock.go
Normal file
62
orbit/pkg/process/process_mock.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package process
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type mockOsProcess struct {
|
||||
mock.Mock
|
||||
OsProcess
|
||||
}
|
||||
|
||||
func (m *mockOsProcess) Signal(sig os.Signal) error {
|
||||
args := m.Called(sig)
|
||||
err := args.Error(0)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return err.(error)
|
||||
}
|
||||
|
||||
func (m *mockOsProcess) Kill() error {
|
||||
args := m.Called()
|
||||
err := args.Error(0)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return err.(error)
|
||||
}
|
||||
|
||||
type mockExecCmd struct {
|
||||
mock.Mock
|
||||
ExecCmd
|
||||
}
|
||||
|
||||
func (m *mockExecCmd) Start() error {
|
||||
args := m.Called()
|
||||
err := args.Error(0)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return err.(error)
|
||||
}
|
||||
|
||||
func (m *mockExecCmd) Wait() error {
|
||||
args := m.Called()
|
||||
err := args.Error(0)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return err.(error)
|
||||
}
|
||||
|
||||
func (m *mockExecCmd) OsProcess() OsProcess {
|
||||
args := m.Called()
|
||||
proc := args.Get(0)
|
||||
if proc == nil {
|
||||
return nil
|
||||
}
|
||||
return proc.(OsProcess)
|
||||
}
|
||||
102
orbit/pkg/process/process_test.go
Normal file
102
orbit/pkg/process/process_test.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWaitOrKillNilProcess(t *testing.T) {
|
||||
// Process not started
|
||||
mockCmd := &mockExecCmd{}
|
||||
defer mock.AssertExpectationsForObjects(t, mockCmd)
|
||||
mockCmd.On("OsProcess").Return(nil)
|
||||
|
||||
p := newWithMock(mockCmd)
|
||||
err := p.WaitOrKill(context.Background(), 1*time.Second)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "non-nil OsProcess")
|
||||
}
|
||||
|
||||
func TestWaitOrKillProcessCompleted(t *testing.T) {
|
||||
// Process already completed
|
||||
mockCmd := &mockExecCmd{}
|
||||
mockProcess := &mockOsProcess{}
|
||||
defer mock.AssertExpectationsForObjects(t, mockCmd, mockProcess)
|
||||
mockCmd.On("OsProcess").Return(mockProcess)
|
||||
mockCmd.On("Wait").Return(nil)
|
||||
|
||||
p := newWithMock(mockCmd)
|
||||
err := p.WaitOrKill(context.Background(), 10*time.Millisecond)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWaitOrKillProcessCompletedError(t *testing.T) {
|
||||
// Process already completed with error
|
||||
mockCmd := &mockExecCmd{}
|
||||
mockProcess := &mockOsProcess{}
|
||||
defer mock.AssertExpectationsForObjects(t, mockCmd, mockProcess)
|
||||
mockCmd.On("OsProcess").Return(mockProcess)
|
||||
mockCmd.On("Wait").After(10 * time.Millisecond).Return(fmt.Errorf("super bad"))
|
||||
|
||||
p := newWithMock(mockCmd)
|
||||
err := p.WaitOrKill(context.Background(), 10*time.Millisecond)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "super bad")
|
||||
}
|
||||
|
||||
func TestWaitOrKillWait(t *testing.T) {
|
||||
// Process completes after the wait call and after the signal is sent
|
||||
mockCmd := &mockExecCmd{}
|
||||
mockProcess := &mockOsProcess{}
|
||||
defer mock.AssertExpectationsForObjects(t, mockCmd, mockProcess)
|
||||
mockCmd.On("OsProcess").Return(mockProcess)
|
||||
mockCmd.On("Wait").After(5 * time.Millisecond).Return(nil)
|
||||
mockProcess.On("Signal", stopSignal()).Return(nil)
|
||||
|
||||
p := newWithMock(mockCmd)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
err := p.WaitOrKill(ctx, 10*time.Millisecond)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "context canceled")
|
||||
}
|
||||
|
||||
func TestWaitOrKillWaitSignalCompleted(t *testing.T) {
|
||||
// Process completes after the wait call and before the signal is sent
|
||||
mockCmd := &mockExecCmd{}
|
||||
mockProcess := &mockOsProcess{}
|
||||
defer mock.AssertExpectationsForObjects(t, mockCmd, mockProcess)
|
||||
mockCmd.On("OsProcess").Return(mockProcess)
|
||||
mockCmd.On("Wait").After(10 * time.Millisecond).Return(nil)
|
||||
mockProcess.On("Signal", stopSignal()).Return(fmt.Errorf("os: process already finished"))
|
||||
|
||||
p := newWithMock(mockCmd)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
err := p.WaitOrKill(ctx, 5*time.Millisecond)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWaitOrKillWaitKilled(t *testing.T) {
|
||||
// Process is killed after the wait call and signal
|
||||
mockCmd := &mockExecCmd{}
|
||||
mockProcess := &mockOsProcess{}
|
||||
defer mock.AssertExpectationsForObjects(t, mockCmd, mockProcess)
|
||||
mockCmd.On("OsProcess").Return(mockProcess)
|
||||
mockCmd.On("Wait").After(10 * time.Millisecond).Return(fmt.Errorf("killed"))
|
||||
mockProcess.On("Signal", stopSignal()).Return(nil)
|
||||
mockProcess.On("Kill").Return(nil)
|
||||
|
||||
p := newWithMock(mockCmd)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
err := p.WaitOrKill(ctx, 5*time.Millisecond)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "context canceled")
|
||||
}
|
||||
69
orbit/pkg/update/badgerstore/badgerstore.go
Normal file
69
orbit/pkg/update/badgerstore/badgerstore.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// package badgerstore implements the go-tuf LocalStore interface using Badger
|
||||
// as a backing store.
|
||||
package badgerstore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
"github.com/theupdateframework/go-tuf/client"
|
||||
)
|
||||
|
||||
const (
|
||||
keyPrefix = ":tuf-metadata:"
|
||||
)
|
||||
|
||||
type badgerStore struct {
|
||||
db *badger.DB
|
||||
}
|
||||
|
||||
// New creates the new store given the badger DB instance.
|
||||
func New(db *badger.DB) client.LocalStore {
|
||||
return &badgerStore{db: db}
|
||||
}
|
||||
|
||||
// SetMeta stores the provided metadata.
|
||||
func (b *badgerStore) SetMeta(name string, meta json.RawMessage) error {
|
||||
if err := b.db.Update(func(tx *badger.Txn) error {
|
||||
if err := tx.Set([]byte(keyPrefix+name), meta); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMeta returns all of the saved metadata.
|
||||
func (b *badgerStore) GetMeta() (map[string]json.RawMessage, error) {
|
||||
res := make(map[string]json.RawMessage)
|
||||
|
||||
// Iterate all keys with matching prefix
|
||||
// Adapted from Badger docs
|
||||
if err := b.db.View(func(txn *badger.Txn) error {
|
||||
it := txn.NewIterator(badger.DefaultIteratorOptions)
|
||||
defer it.Close()
|
||||
prefix := []byte(keyPrefix)
|
||||
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
|
||||
item := it.Item()
|
||||
k := item.Key()
|
||||
|
||||
if err := item.Value(func(v []byte) error {
|
||||
// Remember to strip prefix
|
||||
strippedKey := strings.TrimPrefix(string(k), keyPrefix)
|
||||
res[strippedKey] = json.RawMessage(v)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
31
orbit/pkg/update/badgerstore/badgerstore_test.go
Normal file
31
orbit/pkg/update/badgerstore/badgerstore_test.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package badgerstore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBadgerStore(t *testing.T) {
|
||||
badgerClient, err := badger.Open(badger.DefaultOptions("").WithInMemory(true))
|
||||
require.NoError(t, err)
|
||||
|
||||
store := New(badgerClient)
|
||||
|
||||
expected := map[string]json.RawMessage{
|
||||
"test": json.RawMessage("json"),
|
||||
"test2": json.RawMessage("json2"),
|
||||
"root.json": json.RawMessage(`[{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"0994148e5242118d1d6a9a397a3646e0423545a37794a791c28aa39de3b0c523"}}]`),
|
||||
}
|
||||
|
||||
for k, v := range expected {
|
||||
require.NoError(t, store.SetMeta(k, v))
|
||||
}
|
||||
|
||||
res, err := store.GetMeta()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, res)
|
||||
}
|
||||
16
orbit/pkg/update/file.go
Normal file
16
orbit/pkg/update/file.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package update
|
||||
|
||||
import "os"
|
||||
|
||||
// fileDestination wraps the standard os.File with a Delete method for
|
||||
// compatibility with the go-tuf Destination interface.
|
||||
// Adapted from
|
||||
// https://github.com/theupdateframework/go-tuf/blob/master/cmd/tuf-client/get.go
|
||||
type fileDestination struct {
|
||||
*os.File
|
||||
}
|
||||
|
||||
func (f *fileDestination) Delete() error {
|
||||
_ = f.Close()
|
||||
return os.Remove(f.Name())
|
||||
}
|
||||
94
orbit/pkg/update/filestore/filestore.go
Normal file
94
orbit/pkg/update/filestore/filestore.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package filestore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/theupdateframework/go-tuf/client"
|
||||
)
|
||||
|
||||
type fileStore struct {
|
||||
filename string
|
||||
metadata metadataMap
|
||||
}
|
||||
|
||||
type metadataMap map[string]json.RawMessage
|
||||
|
||||
func New(filename string) (client.LocalStore, error) {
|
||||
store := &fileStore{filename: filename}
|
||||
if err := store.readData(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return store, nil
|
||||
}
|
||||
|
||||
// SetMeta stores the provided metadata.
|
||||
func (s *fileStore) SetMeta(name string, meta json.RawMessage) error {
|
||||
if s.metadata == nil {
|
||||
if err := s.readData(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.metadata[name] = meta
|
||||
if err := s.writeData(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMeta returns all of the saved metadata.
|
||||
func (s *fileStore) GetMeta() (map[string]json.RawMessage, error) {
|
||||
if s.metadata == nil {
|
||||
if err := s.readData(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s.metadata, nil
|
||||
}
|
||||
|
||||
func (s *fileStore) readData() error {
|
||||
stat, err := os.Stat(s.filename)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return errors.Wrap(err, "stat file store")
|
||||
} else if errors.Is(err, os.ErrNotExist) {
|
||||
// initialize empty
|
||||
s.metadata = metadataMap{}
|
||||
return nil
|
||||
} else if !stat.Mode().IsRegular() {
|
||||
return errors.New("expected file store to be regular file")
|
||||
}
|
||||
|
||||
f, err := os.Open(s.filename)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open file store")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var meta metadataMap
|
||||
if err := json.NewDecoder(f).Decode(&meta); err != nil {
|
||||
return errors.Wrap(err, "read file store")
|
||||
}
|
||||
|
||||
s.metadata = meta
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *fileStore) writeData() error {
|
||||
f, err := os.OpenFile(s.filename, os.O_RDWR|os.O_CREATE, constant.DefaultFileMode)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open file store")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := json.NewEncoder(f).Encode(s.metadata); err != nil {
|
||||
return errors.Wrap(err, "read file store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
66
orbit/pkg/update/filestore/filestore_test.go
Normal file
66
orbit/pkg/update/filestore/filestore_test.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package filestore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFileStorePathError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "filestore-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "metadata.json"), constant.DefaultDirMode))
|
||||
|
||||
store, err := New(filepath.Join(tmpDir, "metadata.json"))
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, store)
|
||||
}
|
||||
|
||||
func TestFileStore(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "filestore-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
store, err := New(filepath.Join(tmpDir, "metadata.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := map[string]json.RawMessage{
|
||||
"test": json.RawMessage("{}"),
|
||||
"test2": json.RawMessage("{}"),
|
||||
"root.json": json.RawMessage(`[{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"0994148e5242118d1d6a9a397a3646e0423545a37794a791c28aa39de3b0c523"}}]`),
|
||||
}
|
||||
|
||||
for k, v := range expected {
|
||||
require.NoError(t, store.SetMeta(k, v))
|
||||
}
|
||||
|
||||
res, err := store.GetMeta()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, res)
|
||||
|
||||
// Reopen and check
|
||||
store, err = New(filepath.Join(tmpDir, "metadata.json"))
|
||||
require.NoError(t, err)
|
||||
res, err = store.GetMeta()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, res)
|
||||
|
||||
// Update and check
|
||||
expected["test"] = json.RawMessage("[]")
|
||||
require.NoError(t, store.SetMeta("test", expected["test"]))
|
||||
res, err = store.GetMeta()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, res)
|
||||
}
|
||||
59
orbit/pkg/update/hash.go
Normal file
59
orbit/pkg/update/hash.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"hash"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/theupdateframework/go-tuf/data"
|
||||
)
|
||||
|
||||
// CheckFileHash checks the file at the local path against the provided hash
|
||||
// functions.
|
||||
func CheckFileHash(meta *data.TargetFileMeta, localPath string) error {
|
||||
hashFunc, hashVal, err := selectHashFunction(meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open file for hash")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(hashFunc, f); err != nil {
|
||||
return errors.Wrap(err, "read file for hash")
|
||||
}
|
||||
|
||||
if !bytes.Equal(hashVal, hashFunc.Sum(nil)) {
|
||||
return errors.Errorf("hash %s does not match expected: %s", data.HexBytes(hashFunc.Sum(nil)), data.HexBytes(hashVal))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// selectHashFunction returns the first matching hash function and expected
|
||||
// hash, otherwise returning an error if not matching hash can be found.
|
||||
//
|
||||
// SHA512 is preferred, and SHA256 is returned if 512 is not available.
|
||||
|
||||
func selectHashFunction(meta *data.TargetFileMeta) (hash.Hash, []byte, error) {
|
||||
for hashName, hashVal := range meta.Hashes {
|
||||
if hashName == "sha512" {
|
||||
return sha512.New(), hashVal, nil
|
||||
}
|
||||
}
|
||||
|
||||
for hashName, hashVal := range meta.Hashes {
|
||||
if hashName == "sha256" {
|
||||
return sha256.New(), hashVal, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil, errors.Errorf("no matching hash function found: %v", meta.HashAlgorithms())
|
||||
}
|
||||
23
orbit/pkg/update/options_notwindows.go
Normal file
23
orbit/pkg/update/options_notwindows.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// +build !windows
|
||||
|
||||
package update
|
||||
|
||||
import (
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/theupdateframework/go-tuf/client"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultOptions are the default options to use when creating an update
|
||||
// client.
|
||||
DefaultOptions = Options{
|
||||
RootDirectory: "/var/lib/orbit",
|
||||
ServerURL: defaultURL,
|
||||
RootKeys: defaultRootKeys,
|
||||
LocalStore: client.MemoryLocalStore(),
|
||||
InsecureTransport: false,
|
||||
Platform: constant.PlatformName,
|
||||
OrbitChannel: "stable",
|
||||
OsquerydChannel: "stable",
|
||||
}
|
||||
)
|
||||
31
orbit/pkg/update/options_windows.go
Normal file
31
orbit/pkg/update/options_windows.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/theupdateframework/go-tuf/client"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultOptions are the default options to use when creating an update
|
||||
// client.
|
||||
DefaultOptions = Options{
|
||||
RootDirectory: `C:\Program Files\Orbit`,
|
||||
ServerURL: defaultURL,
|
||||
RootKeys: defaultRootKeys,
|
||||
LocalStore: client.MemoryLocalStore(),
|
||||
InsecureTransport: false,
|
||||
Platform: constant.PlatformName,
|
||||
OrbitChannel: "stable",
|
||||
OsquerydChannel: "stable",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Set root directory to value of ProgramFiles environment variable if not set
|
||||
if dir := os.Getenv("ProgramFiles"); dir != "" {
|
||||
DefaultOptions.RootDirectory = filepath.Join(dir, "Orbit")
|
||||
}
|
||||
}
|
||||
153
orbit/pkg/update/runner.go
Normal file
153
orbit/pkg/update/runner.go
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// RunnerOptions is options provided for the update runner.
|
||||
type RunnerOptions struct {
|
||||
// CheckInterval is the interval to check for updates.
|
||||
CheckInterval time.Duration
|
||||
// Targets is the names of the artifacts to watch for updates.
|
||||
Targets map[string]string
|
||||
}
|
||||
|
||||
// Runner is a specialized runner for the updater. It is designed with Execute and
|
||||
// Interrupt functions to be compatible with oklog/run.
|
||||
type Runner struct {
|
||||
client *Updater
|
||||
opt RunnerOptions
|
||||
cancel chan struct{}
|
||||
hashCache map[string][]byte
|
||||
}
|
||||
|
||||
// NewRunner creates a new runner with the provided options. The runner must be
|
||||
// started with Execute.
|
||||
func NewRunner(client *Updater, opt RunnerOptions) (*Runner, error) {
|
||||
if opt.CheckInterval <= 0 {
|
||||
return nil, errors.New("Runner must be configured with interval greater than 0")
|
||||
}
|
||||
if len(opt.Targets) == 0 {
|
||||
return nil, errors.New("Runner must have nonempty subscriptions")
|
||||
}
|
||||
|
||||
// Initialize hash cache
|
||||
cache := make(map[string][]byte)
|
||||
for target, channel := range opt.Targets {
|
||||
meta, err := client.Lookup(target, channel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "initialize update cache")
|
||||
}
|
||||
|
||||
_, hash, err := selectHashFunction(meta)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "select hash for cache")
|
||||
}
|
||||
cache[target] = hash
|
||||
}
|
||||
|
||||
return &Runner{
|
||||
client: client,
|
||||
opt: opt,
|
||||
|
||||
// chan gets capacity of 1 so we don't end up hung if Interrupt is
|
||||
// called after Execute has already returned.
|
||||
cancel: make(chan struct{}, 1),
|
||||
hashCache: cache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Execute begins a loop checking for updates.
|
||||
func (r *Runner) Execute() error {
|
||||
ticker := time.NewTicker(r.opt.CheckInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Run until cancel or returning an error
|
||||
for {
|
||||
select {
|
||||
case <-r.cancel:
|
||||
return nil
|
||||
|
||||
case <-ticker.C:
|
||||
// On each tick, check for updates
|
||||
didUpdate, err := r.updateAction()
|
||||
if err != nil {
|
||||
log.Info().Err(err).Msg("update failed")
|
||||
}
|
||||
if didUpdate {
|
||||
log.Info().Msg("exiting due to successful update")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) updateAction() (bool, error) {
|
||||
var didUpdate bool
|
||||
if err := r.client.UpdateMetadata(); err != nil {
|
||||
// Consider this a non-fatal error since it will be common to be offline
|
||||
// or otherwise unable to retrieve the metadata.
|
||||
return didUpdate, errors.Wrap(err, "update metadata")
|
||||
}
|
||||
|
||||
for target, channel := range r.opt.Targets {
|
||||
meta, err := r.client.Lookup(target, channel)
|
||||
if err != nil {
|
||||
return didUpdate, errors.Wrapf(err, "lookup failed")
|
||||
}
|
||||
|
||||
// Check whether the hash has changed
|
||||
_, hash, err := selectHashFunction(meta)
|
||||
if err != nil {
|
||||
return didUpdate, errors.Wrap(err, "select hash for cache")
|
||||
}
|
||||
|
||||
if !bytes.Equal(r.hashCache[target], hash) {
|
||||
// Update detected
|
||||
log.Info().Str("target", target).Str("channel", channel).Msg("update detected")
|
||||
if err := r.updateTarget(target, channel); err != nil {
|
||||
return didUpdate, errors.Wrapf(err, "update %s@%s", target, channel)
|
||||
}
|
||||
log.Info().Str("target", target).Str("channel", channel).Msg("update completed")
|
||||
didUpdate = true
|
||||
} else {
|
||||
log.Debug().Str("target", target).Str("channel", channel).Msg("no update")
|
||||
}
|
||||
}
|
||||
|
||||
return didUpdate, nil
|
||||
}
|
||||
|
||||
func (r *Runner) updateTarget(target, channel string) error {
|
||||
path, err := r.client.Get(target, channel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get binary")
|
||||
}
|
||||
|
||||
if target != "orbit" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Symlink Orbit binary
|
||||
linkPath := filepath.Join(r.client.opt.RootDirectory, "bin", "orbit", filepath.Base(path))
|
||||
// Rename the old file otherwise overwrite fails
|
||||
if err := os.Rename(linkPath, linkPath+".old"); err != nil {
|
||||
return errors.Wrap(err, "move old symlink current")
|
||||
}
|
||||
if err := os.Symlink(path, linkPath); err != nil {
|
||||
return errors.Wrap(err, "symlink current")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) Interrupt(err error) {
|
||||
r.cancel <- struct{}{}
|
||||
log.Debug().Msg("interrupt updater")
|
||||
}
|
||||
265
orbit/pkg/update/update.go
Normal file
265
orbit/pkg/update/update.go
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
// package update contains the types and functions used by the update system.
|
||||
package update
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fleetdm/orbit/pkg/constant"
|
||||
"github.com/fleetdm/orbit/pkg/platform"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/theupdateframework/go-tuf/client"
|
||||
"github.com/theupdateframework/go-tuf/data"
|
||||
)
|
||||
|
||||
const (
|
||||
binDir = "bin"
|
||||
stagingDir = "staging"
|
||||
|
||||
defaultURL = "https://tuf.fleetctl.com"
|
||||
defaultRootKeys = `[{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"6d71d3beac3b830be929f2b10d513448d49ec6bb62a680176b89ffdfca180eb4"}}]`
|
||||
)
|
||||
|
||||
// Updater is responsible for managing update state.
|
||||
type Updater struct {
|
||||
opt Options
|
||||
client *client.Client
|
||||
}
|
||||
|
||||
// Options are the options that can be provided when creating an Updater.
|
||||
type Options struct {
|
||||
// RootDirectory is the root directory from which other directories should be referenced.
|
||||
RootDirectory string
|
||||
// ServerURL is the URL of the update server.
|
||||
ServerURL string
|
||||
// InsecureTransport skips TLS certificate verification in the transport if
|
||||
// set to true. Best to leave this on, but due to the file signing any
|
||||
// tampering by a MitM should be detectable.
|
||||
InsecureTransport bool
|
||||
// RootKeys is the JSON encoded root keys to use to bootstrap trust.
|
||||
RootKeys string
|
||||
// LocalStore is the local metadata store.
|
||||
LocalStore client.LocalStore
|
||||
// Platform is the target of the platform to update for. In the default
|
||||
// options this is the current platform.
|
||||
Platform string
|
||||
// OrbitChannel is the update channel to use for Orbit.
|
||||
OrbitChannel string
|
||||
// OsquerydChannel is the update channel to use for osquery (osqueryd).
|
||||
OsquerydChannel string
|
||||
}
|
||||
|
||||
// New creates a new updater given the provided options. All the necessary
|
||||
// directories are initialized.
|
||||
func New(opt Options) (*Updater, error) {
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: opt.InsecureTransport,
|
||||
}
|
||||
httpClient := &http.Client{Transport: transport}
|
||||
|
||||
remoteStore, err := client.HTTPRemoteStore(opt.ServerURL, nil, httpClient)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "init remote store")
|
||||
}
|
||||
|
||||
tufClient := client.NewClient(opt.LocalStore, remoteStore)
|
||||
var rootKeys []*data.Key
|
||||
if err := json.Unmarshal([]byte(opt.RootKeys), &rootKeys); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal root keys")
|
||||
}
|
||||
|
||||
meta, err := opt.LocalStore.GetMeta()
|
||||
if err != nil || meta["root.json"] == nil {
|
||||
var rootKeys []*data.Key
|
||||
if err := json.Unmarshal([]byte(opt.RootKeys), &rootKeys); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal root keys")
|
||||
}
|
||||
if err := tufClient.Init(rootKeys, 1); err != nil {
|
||||
return nil, errors.Wrap(err, "init tuf client")
|
||||
}
|
||||
}
|
||||
|
||||
updater := &Updater{
|
||||
opt: opt,
|
||||
client: tufClient,
|
||||
}
|
||||
|
||||
if err := updater.initializeDirectories(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updater, nil
|
||||
}
|
||||
|
||||
func (u *Updater) UpdateMetadata() error {
|
||||
if _, err := u.client.Update(); err != nil {
|
||||
// An error is returned if we are already up-to-date. We can ignore that
|
||||
// error.
|
||||
if !client.IsLatestSnapshot(errors.Cause(err)) {
|
||||
return errors.Wrap(err, "update metadata")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Updater) RepoPath(target, channel string) string {
|
||||
return path.Join(target, u.opt.Platform, channel, target+constant.ExecutableExtension(u.opt.Platform))
|
||||
}
|
||||
|
||||
func (u *Updater) LocalPath(target, channel string) string {
|
||||
return u.pathFromRoot(filepath.Join(binDir, target, u.opt.Platform, channel, target+constant.ExecutableExtension(u.opt.Platform)))
|
||||
}
|
||||
|
||||
// Lookup looks up the provided target in the local target metadata. This should
|
||||
// be called after UpdateMetadata.
|
||||
func (u *Updater) Lookup(target, channel string) (*data.TargetFileMeta, error) {
|
||||
t, err := u.client.Target(u.RepoPath(target, channel))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "lookup %s@%s", target, channel)
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
// Targets gets all of the known targets
|
||||
func (u *Updater) Targets() (data.TargetFiles, error) {
|
||||
targets, err := u.client.Targets()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get targets")
|
||||
}
|
||||
|
||||
return targets, nil
|
||||
}
|
||||
|
||||
// Get returns the local path to the specified target. The target is downloaded
|
||||
// if it does not yet exist locally or the hash does not match.
|
||||
func (u *Updater) Get(target, channel string) (string, error) {
|
||||
if target == "" {
|
||||
return "", errors.New("target is required")
|
||||
}
|
||||
if channel == "" {
|
||||
return "", errors.New("channel is required")
|
||||
}
|
||||
|
||||
localPath := u.LocalPath(target, channel)
|
||||
repoPath := u.RepoPath(target, channel)
|
||||
stat, err := os.Stat(localPath)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("stat file")
|
||||
return localPath, u.Download(repoPath, localPath)
|
||||
}
|
||||
if !stat.Mode().IsRegular() {
|
||||
return "", errors.Errorf("expected %s to be regular file", localPath)
|
||||
}
|
||||
|
||||
meta, err := u.Lookup(target, channel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := CheckFileHash(meta, localPath); err != nil {
|
||||
log.Debug().Str("info", err.Error()).Msg("change detected")
|
||||
return localPath, u.Download(repoPath, localPath)
|
||||
}
|
||||
|
||||
log.Debug().Str("path", localPath).Str("target", target).Str("channel", channel).Msg("found expected target locally")
|
||||
|
||||
return localPath, nil
|
||||
}
|
||||
|
||||
// Download downloads the target to the provided path. The file is deleted and
|
||||
// an error is returned if the hash does not match.
|
||||
func (u *Updater) Download(repoPath, localPath string) error {
|
||||
staging := filepath.Join(u.opt.RootDirectory, stagingDir)
|
||||
|
||||
if err := os.MkdirAll(staging, constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "initialize download dir")
|
||||
}
|
||||
|
||||
// Additional chmod only necessary on Windows, effectively a no-op on other
|
||||
// platforms.
|
||||
if err := platform.ChmodExecutableDirectory(staging); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmp, err := os.OpenFile(
|
||||
filepath.Join(staging, filepath.Base(localPath)),
|
||||
os.O_CREATE|os.O_WRONLY,
|
||||
constant.DefaultExecutableMode,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open temp file for download")
|
||||
}
|
||||
defer func() {
|
||||
tmp.Close()
|
||||
os.Remove(tmp.Name())
|
||||
}()
|
||||
if err := platform.ChmodExecutable(tmp.Name()); err != nil {
|
||||
return errors.Wrap(err, "chmod download")
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(localPath), constant.DefaultDirMode); err != nil {
|
||||
return errors.Wrap(err, "initialize download dir")
|
||||
}
|
||||
|
||||
// Additional chmod only necessary on Windows, effectively a no-op on other
|
||||
// platforms.
|
||||
if err := platform.ChmodExecutableDirectory(filepath.Dir(localPath)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The go-tuf client handles checking of max size and hash.
|
||||
if err := u.client.Download(repoPath, &fileDestination{tmp}); err != nil {
|
||||
return errors.Wrapf(err, "download target %s", repoPath)
|
||||
}
|
||||
if err := tmp.Close(); err != nil {
|
||||
return errors.Wrap(err, "close tmp file")
|
||||
}
|
||||
|
||||
// Attempt to exec the new binary only if the platform matches. This will
|
||||
// always fail if the binary doesn't match the platform, so there's not
|
||||
// really anything we can check.
|
||||
if u.opt.Platform == constant.PlatformName {
|
||||
out, err := exec.Command(tmp.Name(), "--version").CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "exec new version: %s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
if constant.PlatformName == "windows" {
|
||||
// Remove old file first
|
||||
if err := os.Rename(localPath, localPath+".old"); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return errors.Wrap(err, "rename old")
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Rename(tmp.Name(), localPath); err != nil {
|
||||
return errors.Wrap(err, "move download")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Updater) pathFromRoot(parts ...string) string {
|
||||
return filepath.Join(append([]string{u.opt.RootDirectory}, parts...)...)
|
||||
}
|
||||
|
||||
func (u *Updater) initializeDirectories() error {
|
||||
for _, dir := range []string{
|
||||
u.pathFromRoot(binDir),
|
||||
} {
|
||||
err := os.MkdirAll(dir, constant.DefaultDirMode)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "initialize directories")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
59
orbit/pkg/update/update_test.go
Normal file
59
orbit/pkg/update/update_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInitializeDirectories(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "orbit-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
opt := DefaultOptions
|
||||
opt.RootDirectory = tmpDir
|
||||
updater := Updater{opt: opt}
|
||||
err = updater.initializeDirectories()
|
||||
require.NoError(t, err)
|
||||
assertDir(t, filepath.Join(tmpDir, binDir))
|
||||
}
|
||||
|
||||
func assertDir(t *testing.T, path string) {
|
||||
info, err := os.Stat(path)
|
||||
assert.NoError(t, err, "stat should succeed")
|
||||
assert.True(t, info.IsDir())
|
||||
}
|
||||
|
||||
func TestMakeRepoPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
version string
|
||||
platform string
|
||||
expected string
|
||||
}{
|
||||
{platform: "linux", name: "osqueryd", version: "4.6.0", expected: "osqueryd/linux/4.6.0/osqueryd"},
|
||||
{platform: "linux", name: "osqueryd", version: "3.3.2", expected: "osqueryd/linux/3.3.2/osqueryd"},
|
||||
{platform: "macos", name: "osqueryd", version: "4.6.0", expected: "osqueryd/macos/4.6.0/osqueryd"},
|
||||
{platform: "macos", name: "osqueryd", version: "3.3.2", expected: "osqueryd/macos/3.3.2/osqueryd"},
|
||||
{platform: "windows", name: "osqueryd", version: "4.6.0", expected: "osqueryd/windows/4.6.0/osqueryd.exe"},
|
||||
{platform: "windows", name: "osqueryd", version: "3.3.2", expected: "osqueryd/windows/3.3.2/osqueryd.exe"},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.expected, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := Updater{opt: Options{Platform: tt.platform}}
|
||||
assert.Equal(t, tt.expected, u.RepoPath(tt.name, tt.version))
|
||||
})
|
||||
}
|
||||
}
|
||||
25
orbit/tools/build/sign-macos.sh
Executable file
25
orbit/tools/build/sign-macos.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
if [ -z "$CODESIGN_IDENTITY" ]
|
||||
then
|
||||
echo 'Must set CODESIGN_IDENTITY in environment'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$1" ]
|
||||
then
|
||||
echo 'First argument must be path to binary'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Skip if not a macOS Mach-O executable
|
||||
if ! ( file "$1" | grep Mach-O )
|
||||
then
|
||||
echo 'Skip macOS signing'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
codesign -s "$CODESIGN_IDENTITY" -i com.fleetdm.orbit -f -v --timestamp --options runtime "$1"
|
||||
|
||||
echo "Signed successfully"
|
||||
6
orbit/tools/cleanup/cleanup_macos.sh
Executable file
6
orbit/tools/cleanup/cleanup_macos.sh
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
sudo launchctl stop com.fleetdm.orbit
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.fleetdm.orbit.plist
|
||||
|
||||
sudo rm -rf /Library/LaunchDaemons/com.fleetdm.orbit.plist /var/lib/orbit/ /usr/local/bin/orbit /var/log/orbit
|
||||
5
orbit/tools/ubuntu/Dockerfile
Normal file
5
orbit/tools/ubuntu/Dockerfile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
FROM ubuntu
|
||||
|
||||
RUN apt-get update &&\
|
||||
apt-get install -y ca-certificates &&\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
Loading…
Reference in a new issue