Browse Source

Checkpoint

master
maze 1 year ago
parent
commit
0d1fdad6fd
103 changed files with 24667 additions and 182 deletions
  1. +3
    -0
      .idea/.gitignore
  2. +5
    -0
      .idea/codeStyles/codeStyleConfig.xml
  3. +11
    -0
      .idea/gate.iml
  4. +6
    -0
      .idea/misc.xml
  5. +8
    -0
      .idea/modules.xml
  6. +6
    -0
      .idea/vcs.xml
  7. +29
    -0
      .idea/watcherTasks.xml
  8. +3
    -1
      cmd/gate/main.go
  9. +7
    -0
      etc/gate/auth/file.hcl
  10. +19
    -0
      etc/gate/auth/radius.hcl
  11. +10
    -0
      etc/gate/auth/yubikey.hcl
  12. +61
    -0
      etc/gate/credential/certificate.hcl
  13. +28
    -0
      etc/gate/credential/vault.hcl
  14. +43
    -121
      etc/gate/gate.hcl
  15. +3
    -0
      etc/gate/roles.hcl
  16. +36
    -0
      gate.toml
  17. +10
    -4
      go.mod
  18. +156
    -0
      go.sum
  19. +10
    -0
      gosec.json
  20. +53
    -0
      pkg/core/auth.go
  21. +61
    -9
      pkg/core/cli/config.go
  22. +25
    -0
      pkg/core/host.go
  23. +7
    -0
      pkg/core/runner.go
  24. +3
    -0
      pkg/core/service.go
  25. +16
    -1
      pkg/core/user.go
  26. +31
    -0
      pkg/crypto/authority/ca.go
  27. +95
    -0
      pkg/crypto/ksm/hsm.go
  28. +192
    -0
      pkg/crypto/ksm/ksm.go
  29. +88
    -0
      pkg/crypto/ksm/util.go
  30. +12
    -0
      pkg/db/db.go
  31. +121
    -0
      pkg/db/sqlite.go
  32. +28
    -0
      pkg/db/sqlite_test.go
  33. +23
    -0
      pkg/net/mdns/.gitignore
  34. +20
    -0
      pkg/net/mdns/LICENSE
  35. +37
    -0
      pkg/net/mdns/README.md
  36. +347
    -0
      pkg/net/mdns/client.go
  37. +288
    -0
      pkg/net/mdns/server.go
  38. +59
    -0
      pkg/net/mdns/server_test.go
  39. +307
    -0
      pkg/net/mdns/zone.go
  40. +275
    -0
      pkg/net/mdns/zone_test.go
  41. +2
    -1
      pkg/net/secureshell/server.go
  42. +15
    -5
      pkg/net/secureshell/transport.go
  43. +17
    -5
      pkg/net/web/auth.go
  44. +145
    -23
      pkg/net/web/server.go
  45. +350
    -0
      pkg/net/web/socket.go
  46. +1
    -0
      pkg/plugin/all/all.go
  47. +72
    -0
      pkg/plugin/common/common.go
  48. +25
    -10
      pkg/plugin/credential/cert.go
  49. +137
    -0
      pkg/plugin/hosts/hostfile.go
  50. +3
    -0
      pkg/plugin/hosts/hostfile_posix.go
  51. +3
    -0
      pkg/plugin/hosts/hosts_window.go
  52. +168
    -0
      pkg/plugin/hosts/mdns.go
  53. +64
    -0
      pkg/text/charset.go
  54. +193
    -0
      pkg/text/compare.go
  55. +19
    -0
      test/plugin-host/main.go
  56. +2
    -2
      testdata/groups
  57. +1
    -0
      testdata/users
  58. +1
    -0
      testdata/vault.token
  59. +17
    -0
      ui/.babelrc
  60. +9
    -0
      ui/.editorconfig
  61. +5
    -0
      ui/.eslintignore
  62. +29
    -0
      ui/.eslintrc.js
  63. +15
    -0
      ui/.gitignore
  64. +10
    -0
      ui/.postcssrc.js
  65. +27
    -0
      ui/README.md
  66. +41
    -0
      ui/build/build.js
  67. +54
    -0
      ui/build/check-versions.js
  68. BIN
      ui/build/logo.png
  69. +101
    -0
      ui/build/utils.js
  70. +22
    -0
      ui/build/vue-loader.conf.js
  71. +92
    -0
      ui/build/webpack.base.conf.js
  72. +95
    -0
      ui/build/webpack.dev.conf.js
  73. +149
    -0
      ui/build/webpack.prod.conf.js
  74. +7
    -0
      ui/config/dev.env.js
  75. +96
    -0
      ui/config/index.js
  76. +4
    -0
      ui/config/prod.env.js
  77. +7
    -0
      ui/config/test.env.js
  78. +14
    -0
      ui/index.html
  79. +18960
    -0
      ui/package-lock.json
  80. +87
    -0
      ui/package.json
  81. +114
    -0
      ui/src/App.vue
  82. +77
    -0
      ui/src/Sidebar.vue
  83. BIN
      ui/src/assets/logo.png
  84. +9
    -0
      ui/src/assets/theme.scss
  85. +34
    -0
      ui/src/backend/vue-axios/axios.js
  86. +8
    -0
      ui/src/backend/vue-axios/index.js
  87. +83
    -0
      ui/src/components/Dashboard.vue
  88. +81
    -0
      ui/src/components/Hosts.vue
  89. +152
    -0
      ui/src/components/SSH.vue
  90. +100
    -0
      ui/src/components/Transport.vue
  91. +79
    -0
      ui/src/components/Transports.vue
  92. +9
    -0
      ui/src/components/Tunnel.vue
  93. +25
    -0
      ui/src/main.js
  94. +23
    -0
      ui/src/models/User.js
  95. +57
    -0
      ui/src/router/index.js
  96. +40
    -0
      ui/src/store/auth.js
  97. +12
    -0
      ui/src/store/index.js
  98. +2
    -0
      ui/src/store/mutation-types.js
  99. +131
    -0
      ui/src/views/Login.vue
  100. +14
    -0
      ui/src/views/Logout.vue

+ 3
- 0
.idea/.gitignore View File

@ -0,0 +1,3 @@
# Default ignored files
/workspace.xml

+ 5
- 0
.idea/codeStyles/codeStyleConfig.xml View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

+ 11
- 0
.idea/gate.iml View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Bundled Protobuf Distribution" level="application" />
</component>
</module>

+ 6
- 0
.idea/misc.xml View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
</project>

+ 8
- 0
.idea/modules.xml View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/gate.iml" filepath="$PROJECT_DIR$/.idea/gate.iml" />
</modules>
</component>
</project>

+ 6
- 0
.idea/vcs.xml View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

+ 29
- 0
.idea/watcherTasks.xml View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions">
<TaskOptions isEnabled="true">
<option name="arguments" value="-local maze.io/gate -w $FilePath$" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="go" />
<option name="immediateSync" value="false" />
<option name="name" value="goimports" />
<option name="output" value="$FilePath$" />
<option name="outputFilters">
<array />
</option>
<option name="outputFromStdout" value="false" />
<option name="program" value="goimports" />
<option name="runOnExternalChanges" value="false" />
<option name="scopeName" value="Project Files" />
<option name="trackOnlyRoot" value="true" />
<option name="workingDir" value="$ProjectFileDir$" />
<envs>
<env name="GOROOT" value="$GOROOT$" />
<env name="GOPATH" value="$GOPATH$" />
<env name="PATH" value="$GoBinDirs$" />
</envs>
</TaskOptions>
</component>
</project>

+ 3
- 1
cmd/gate/main.go View File

@ -7,9 +7,11 @@ import (
"maze.io/gate/pkg/core/cli"
)
var DefaultConfig = "etc/gate/gate.hcl"
func main() {
var (
configFlag = flag.String("config", "gate.hcl", "configuration file")
configFlag = flag.String("config", DefaultConfig, "configuration file")
)
flag.Parse()


+ 7
- 0
etc/gate/auth/file.hcl View File

@ -0,0 +1,7 @@
file {
# Users file path, in UNIX passwd format
users = "testdata/users"
# Groups file path, in UNIX group format
groups = "testdata/groups"
}

+ 19
- 0
etc/gate/auth/radius.hcl View File

@ -0,0 +1,19 @@
radius {
# Server is the address of the RADIUS server
server = "127.0.0.1"
# Servers specifies the addresses of the RADIUS servers pool
#servers = ["10.0.0.1", "10.0.0.2"]
# Secret is the shared secret
secret = "testing"
# Station identifier, defaults to the hostname of the system
#station = "gate-001"
# Timeout specifies the request timeout
#timeout = "5s"
# Prompt for keyboard-interactive challenges
prompt = "Password: "
}

+ 10
- 0
etc/gate/auth/yubikey.hcl View File

@ -0,0 +1,10 @@
yubikey {
# Server specifies the YubiKey Validation API server
#server = "https://api.yubico.com"
# Servers specifies a pool of YubiKey Validation API servers
#servers = ["https://emea.auth.maze.network", "https://asia.auth.maze.network"]
# Prompt for keyboard-interactive challenges
prompt = "Token: "
}

+ 61
- 0
etc/gate/credential/certificate.hcl View File

@ -0,0 +1,61 @@
# The certificate credential provider generates ephemeral keys and signs a
# certificate for the target connection.
#
# This only works if:
# * The SSH server has the signing public key configured in
# `authorized_keys`, and/or:
# * The SSH server has the signing public key configured in the OpenSSH
# daemon, using the `TrustedUserCAKeys` stanza. See the `sshd_config`
# manual for more information.
certificate {
# Key for signing
key = "testdata/ssh_host_ed25519_key"
# Key for signing user certificates. If not specified, the key above is
# used.
#user_key = "testdata/user_ca_ed25519_key"
# Key for signing host certificates. If not specified, the key above is
# used.
#host_key = "testdata/host_ca_ed25519_key"
# Key type is the type of ephemeral key that is generated, valid values
# are:
# - ed25519
# - rsa2048
# - rsa4096
#
# The RSA types have better support, but the Ed25519 keys are
# computationally less expensive and offers better security than ECDSA
# and DSA.
key_type = "rsa2048"
# Prefix for the key ID.
# For example "@maze.network" to issue key ID's "login@maze.network".
#id_prefix = ""
# Suffix for the key ID.
# For example "MAZEIO\\" to issue key ID's "MAZEIO\login".
#id_suffix = ""
# Some implementations require the group to be encoded as principal.
groups_as_principals = true
/*
# Extensions is a map of group-to-extensions.
# The zero value ("") determines the default extensions.
extensions "" {
permit-X11-forwarding = ""
permit-agent-forwarding = ""
permit-port-forwarding = ""
permit-pty = ""
permit-user-rc = ""
}
# Options is a map of group-to-options.
# The zero value ("") determines the default options.
options "" {
# There are no default options.
}
*/
}

+ 28
- 0
etc/gate/credential/vault.hcl View File

@ -0,0 +1,28 @@
# The Vault provider makes use of the Vault SSH Secrets Engine.
# See https://www.vaultproject.io/docs/secrets/ssh/index.html
/*
vault {
# addr is the Vault address.
# It defaults to $VAULT_ADDR.
#addr = "https://127.0.0.1:8200"
# token is the Vault token.
# It defaults to $VAULT_TOKEN, if TokenFile is not set.
#token = "this is my secret vault token"
# token_file is a file that contains the Vault token.
#token_file = "/etc/gate/secret/vault.token"
# insecure disables TLS verification.
# It defaults to $VAULT_SKIP_VERIFY.
#insecure = true
# mount is the SSH Secrets Engine mount point.
# It defaults to "/ssh".
#mount = "/ssh"
# role is the Vault role.
# It defaults to "gate".
#role = "gate"
}
*/

gate.hcl → etc/gate/gate.hcl View File

@ -77,6 +77,10 @@ httpd {
# they will not be able to log in.
#url = "https://gate.maze.network"
# Secret for encrypting cookies.
# Use for example `openssh rand -hex 16` to generate a 128-bit key.
secret = "5823f211e22e8dc8ad6534f1718ce950"
# TLS enables HTTPS (recommended).
tls {
# Cert is the path to the TLS certificate file
@ -115,48 +119,33 @@ httpd {
#
#--------------------------------------------------------------------------
auth "file" {
# Users file path, in UNIX passwd format
users = "testdata/users"
# Groups file path, in UNIX group format
groups = "testdata/groups"
auth "system" {
# The system provider has no configuration
}
auth "radius" {
# Server is the address of the RADIUS server
server = "127.0.0.1"
# Auth provider from external files.
auth_include = ["etc/gate/auth/*.hcl"]
# Servers specifies the addresses of the RADIUS servers pool
#servers = ["10.0.0.1", "10.0.0.2"]
# Secret is the shared secret
secret = "testing"
# Station identifier, defaults to the hostname of the system
#station = "gate-001"
# Timeout specifies the request timeout
#timeout = "5s"
#--------------------------------------------------------------------------
#
# Roles
#
#--------------------------------------------------------------------------
# Prompt for keyboard-interactive challenges
prompt = "Password: "
}
# Role provided by the configuration.
/*
role "admin" {
# Users belonging to this role.
#users = ["jdoe"]
auth "system" {
# The system provider has no configuration
# Groups belonging to this role.
groups = ["admin"]
}
*/
auth "yubikey" {
# Server specifies the YubiKey Validation API server
#server = "https://api.yubico.com"
# Servers specifies a pool of YubiKey Validation API servers
#servers = ["https://emea.auth.maze.network", "https://asia.auth.maze.network"]
# Prompt for keyboard-interactive challenges
prompt = "Token: "
}
# Roles from an external file.
role_include = [ "etc/gate/roles.hcl" ]
#--------------------------------------------------------------------------
#
@ -179,69 +168,6 @@ credential "agent" {
# The agent credential provider has no configuration
}
# The certificate credential provider generates ephemeral keys and signs a
# certificate for the target connection.
#
# This only works if:
# * The SSH server has the signing public key configured in
# `authorized_keys`, and/or:
# * The SSH server has the signing public key configured in the OpenSSH
# daemon, using the `TrustedUserCAKeys` stanza. See the `sshd_config`
# manual for more information.
credential "certificate" {
# Key for signing
key = "testdata/ssh_host_ed25519_key"
# Key for signing user certificates. If not specified, the key above is
# used.
#user_key = "testdata/user_ca_ed25519_key"
# Key for signing host certificates. If not specified, the key above is
# used.
#host_key = "testdata/host_ca_ed25519_key"
# Key type is the type of ephemeral key that is generated, valid values
# are:
# - ed25519
# - rsa2048
# - rsa4096
#
# The RSA types have better support, but the Ed25519 keys are
# computationally less expensive and offers better security than ECDSA
# and DSA.
key_type = "rsa2048"
# Prefix for the key ID.
# For example "@maze.network" to issue key ID's "login@maze.network".
#id_prefix = ""
# Suffix for the key ID.
# For example "MAZEIO\\" to issue key ID's "MAZEIO\login".
#id_suffix = ""
# Some broken implementations require the group to be encoded as
# principal.
#groups_as_principals = false
/*
# Extensions is a map of group-to-extensions.
# The zero value ("") determines the default extensions.
extensions "" {
permit-X11-forwarding = ""
permit-agent-forwarding = ""
permit-port-forwarding = ""
permit-pty = ""
permit-user-rc = ""
}
# Options is a map of group-to-options.
# The zero value ("") determines the default options.
options "" {
# There are no default options.
}
*/
}
# The password provider will prompt the user for a password.
/*
credential "password" {
@ -250,31 +176,9 @@ credential "password" {
}
*/
# The Vault provider makes use of the Vault SSH Secrets Engine.
# See https://www.vaultproject.io/docs/secrets/ssh/index.html
credential "vault" {
# addr is the Vault address.
# It defaults to $VAULT_ADDR.
addr = "https://172.23.42.3:8200"
# Credential providers from external files.
credential_include = ["etc/gate/credential/*.hcl"]
# token is the Vault token.
# It defaults to $VAULT_TOKEN, if TokenFile is not set.
#token = "s.sMNOTrwqkmdeOa7aE7jBfnTN"
# token_file is a file that contains the Vault token.
token_file = "testdata/vault.token"
# insecure disables TLS verification.
# It defaults to $VAULT_SKIP_VERIFY.
insecure = true
# mount is the SSH Secrets Engine mount point.
mount = "/ssh"
# role is the Vault role.
# It defaults to "gate".
role = "gate"
}
#--------------------------------------------------------------------------
#
@ -296,6 +200,24 @@ identity {
groups = "testdata/groups"
}
#--------------------------------------------------------------------------
#
# Hosts
#
#--------------------------------------------------------------------------
# Hostfile resolves hosts using a hosts(5) file format.
hosts "hostfile" {
# Path to the hosts file.
# Defaults to "/etc/hosts" on POSIX compatible systems.
# Defaults to "${SystemRoot}\\System32\\drivers\\etc\\hosts" on Windows.
#path = "/etc/hosts"
}
hosts "mdns" {
}
#--------------------------------------------------------------------------
#
# Policies

+ 3
- 0
etc/gate/roles.hcl View File

@ -0,0 +1,3 @@
admin = {
group = "admin"
}

+ 36
- 0
gate.toml View File

@ -111,6 +111,42 @@ type = "file"
users = "testdata/users"
groups = "testdata/groups"
# policies
# ========
# matches ssh root target users:
[[policy.ssh.user]]
match = "root"
# use the "certificate" credential provider
credential = "certificate"
# permit groups and roles
permit_groups = ["admin", "staff", "wheel"]
permit_roles = ["admin"]
# match ssh test target users:
[[policy.ssh.user]]
match = "test*"
# reject groups
reject_groups = ["admin", "staff", "wheel"]
# matches all ssh target users:
[[policy.ssh.user]]
match = "*"
# use the "certificate" credential provider
credential = "certificate"
# match ssh localhost target hosts:
[policy.ssh.host."127.0.0.0/8"]
reject = { groups = ["admin", "staff", "wheel"] }
[policy.ssh.host."::1"]
reject = { groups = ["admin", "staff", "wheel"] }
# match any tcp target host:
[policy.tcp.host."0.0.0.0/0"]
permit_protos = ["ssh"]
# mesh clustering
# ===============
#[mesh.network]


+ 10
- 4
go.mod View File

@ -3,31 +3,37 @@ module maze.io/gate
go 1.13
require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46
github.com/ThalesIgnite/crypto11 v1.2.0 // indirect
github.com/c-bata/go-prompt v0.2.3
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/golang/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.3.2
github.com/googollee/go-socket.io v1.4.2
github.com/gorilla/sessions v1.2.0
github.com/gorilla/websocket v1.4.1
github.com/hashicorp/golang-lru v0.5.3
github.com/hashicorp/hcl v1.0.0
github.com/hashicorp/mdns v1.0.1
github.com/hashicorp/memberlist v0.1.4
github.com/kr/pretty v0.1.0 // indirect
github.com/jmoiron/sqlx v1.2.0
github.com/labstack/echo-contrib v0.6.0
github.com/labstack/echo/v4 v4.1.10
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/mattn/go-sqlite3 v1.11.0
github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859 // indirect
github.com/miekg/dns v1.0.14
github.com/mitchellh/mapstructure v1.1.2
github.com/natefinch/lumberjack v2.0.0+incompatible
github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/stretchr/testify v1.4.0
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b // indirect
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5
golang.org/x/text v0.3.2
google.golang.org/grpc v1.23.1
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/ldap.v2 v2.5.1


+ 156
- 0
go.sum View File

@ -1,24 +1,65 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.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.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46 h1:rs0kDBt2zF4/CM9rO5/iH+U22jnTygPlqWgX55Ufcxg=
github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/ThalesIgnite/crypto11 v1.2.0 h1:MNljmS3B9EmSFLZ4I7IFueeJNU5g869d7LcWvNrhv9Q=
github.com/ThalesIgnite/crypto11 v1.2.0/go.mod h1:vmlYtalkn8uCp3eStRZ0r7Sslmf1jAtL8De0PIyqPks=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/c-bata/go-prompt v0.2.3 h1:jjCS+QhG/sULBhAaBdjb2PlMRVaKXQgn+4yzaauvs2s=
github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/casbin/casbin v1.8.2/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
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/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googollee/go-engine.io v1.4.1 h1:m3WlZAug1SODuWT++UX2nbzk9IUCn9T1SnmHoqppdqo=
github.com/googollee/go-engine.io v1.4.1/go.mod h1:26oFqHsnuWIzNOM0T08x21eQOydBosKOCgK3tyhzPPI=
github.com/googollee/go-socket.io v1.4.2 h1:yxoY4jyPp91ksh0IDBOLrphRXg6EXG+QUdveqg1RGSc=
github.com/googollee/go-socket.io v1.4.2/go.mod h1:yjlQxKcAZXZjpGwQVW/y1sgyL1ou+DdCpkswURDCRrU=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
@ -26,6 +67,12 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
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-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
@ -43,13 +90,20 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
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/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/memberlist v0.1.4 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs=
github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
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/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo-contrib v0.6.0 h1:WT+TwJkJXrK+9n+x5VcI8f17VkUsiY33H5Mw3Pe8OfI=
@ -60,6 +114,7 @@ github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvf
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@ -67,24 +122,66 @@ github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859 h1:smQbSzmT3EHl4EUwtFwFGmGIpiYgIiiPeVv1uguIQEE=
github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ=
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 h1:A7GG7zcGjl3jqAqGPmcNjd/D9hzL95SuoOQAaFNdLU0=
github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
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/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
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/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@ -93,19 +190,36 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/thales-e-security/pool v0.0.1 h1:1eJJNN2K/mAzwfr546brAiQVa3UaRC0gGENsHM8veS8=
github.com/thales-e-security/pool v0.0.1/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/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 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -113,12 +227,21 @@ golang.org/x/net v0.0.0-20190607181551-461777fb6f67 h1:rJJxsykSlULwd2P2+pg/rtnwN
golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
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-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20181029174526-d69651ed3497/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -133,25 +256,56 @@ golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5 h1:SW/0nsKCUaozCUtZTakri5lao
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/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-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190214204934-8dcb7bc8c7fe/go.mod h1:E6PF97AdD6v0s+fPshSmumCW1S1Ne85RbPQxELkKa44=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190610214847-0945d3616f18/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU=
gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
layeh.com/radius v0.0.0-20190322222518-890bc1058917 h1:BDXFaFzUt5EIqe/4wrTc4AcYZWP6iC6Ult+jQWLh5eU=
layeh.com/radius v0.0.0-20190322222518-890bc1058917/go.mod h1:fywZKyu//X7iRzaxLgPWsvc0L26IUpVvE/aeIL2JtIQ=
maze.io/x/asciicast v1.0.1 h1:oYekeRlWiFu4PSxrCLC5hBDjgRm3KMFOi57YmFMjbg4=
@ -160,3 +314,5 @@ maze.io/x/otp v0.0.0-20190910213007-b6cf3cc7246a h1:QbersYNc6Zwr03mawjeX50PCA15e
maze.io/x/otp v0.0.0-20190910213007-b6cf3cc7246a/go.mod h1:WdKOzlyiJegybpv2TdYGxW/QSHzn2zZUEp6Hc9jut4o=
maze.io/x/secureshell v1.0.8 h1:cu4pY0KQd9u6hK4vsyJdOIEjGy0Crj04F8S+TVjXozw=
maze.io/x/secureshell v1.0.8/go.mod h1:jD54Uod5w7PCNrQgoP87iHQF8JyyB7E9XRymFKrXmsU=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

+ 10
- 0
gosec.json View File

@ -0,0 +1,10 @@
{
"G104": {
"maze.io/gate/pkg/crypto/crypt": [
"Generate"
],
"maze.io/gate/pkg/text/terminfo/dynamic": [
"unescape"
]
}
}

+ 53
- 0
pkg/core/auth.go View File

@ -16,11 +16,15 @@ const DefaultAuthenticationProviderName = "system"
// DefaultCredentialProviderName is the name of the default credential provider.
const DefaultCredentialProviderName = "agent"
// DefaultHostsProviderName is the name of the default hosts provider.
const DefaultHostsProviderName = "hostfile"
// Providers is a collection of configured providers.
type Providers struct {
Identity IdentityProvider
Authentication map[string]AuthenticationProvider
Credential map[string]CredentialProvider
Hosts map[string]HostsProvider
}
// AuthenticationProviderConfig is the configuration structure for "auth" blocks.
@ -170,3 +174,52 @@ func NewCredentialProvider(name string, config map[string]interface{}, log *logg
}
return provider, nil
}
// HostsProviderConfig is the configuration structure for "hosts" blocks.
type HostsProviderConfig map[string]map[string]interface{}
// Providers returns the configured credential providers keyed by name.
func (config HostsProviderConfig) Providers(log *logger.Logger) (map[string]HostsProvider, error) {
var (
providers = make(map[string]HostsProvider)
names []string
)
if config == nil {
p, err := NewHostsProvider(DefaultHostsProviderName, nil, log)
if err != nil {
return nil, err
}
providers[DefaultHostsProviderName] = p
names = append(names, DefaultHostsProviderName)
} else {
for name, componentConfig := range config {
p, err := NewHostsProvider(name, componentConfig, log)
if err != nil {
return nil, err
}
providers[name] = p
names = append(names, name)
}
}
log.WithField("providers", names).Debug("loaded hosts providers")
return providers, nil
}
// NewHostsProvider loads a hosts provider by name.
func NewHostsProvider(name string, config map[string]interface{}, log *logger.Logger) (HostsProvider, error) {
log.WithField(logger.Component, name).Debug("loading hosts provider")
component, err := LoadPlugin(name)
if err != nil {
return nil, err
}
provider, ok := component.(HostsProvider)
if !ok {
return nil, fmt.Errorf("core: component %q does not implement HostsProvider", name)
}
if err := provider.Setup(config, log); err != nil {
return nil, err
}
return provider, nil
}

+ 61
- 9
pkg/core/cli/config.go View File

@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/hashicorp/hcl"
@ -18,15 +19,21 @@ import (
// Config is the top configuration structure.
type Config struct {
Mesh *mesh.Mesh `hcl:"mesh" toml:"mesh"`
SecureShell *secureshell.Server `hcl:"sshd" toml:"sshd"`
Web *web.Server `hcl:"httpd" toml:"httpd"`
Log logger.Config `hcl:"log" toml:"log"`
Auth core.AuthenticationProviderConfig `hcl:"auth" toml:"auth"`
Identity core.IdentityProviderConfig `hcl:"identity" toml:"identity"`
Credential core.CredentialProviderConfig `hcl:"credential" toml:"credential"`
Roles core.Roles `hcl:"roles" toml:"roles"`
Recorder *recorder.Recorder `hcl:"recorder" toml:"recorder"`
Mesh *mesh.Mesh `hcl:"mesh"`
SecureShell *secureshell.Server `hcl:"sshd"`
Web *web.Server `hcl:"httpd"`
Log logger.Config `hcl:"log"`
Auth core.AuthenticationProviderConfig `hcl:"auth"`
AuthInclude []string `hcl:"auth_include"`
Identity core.IdentityProviderConfig `hcl:"identity"`
IdentityInclude []string `hcl:"identity_include"`
Credential core.CredentialProviderConfig `hcl:"credential"`
CredentialInclude []string `hcl:"credential_include"`
Role core.Roles `hcl:"role"`
RoleInclude []string `hcl:"role_include"`
Hosts core.HostsProviderConfig `hcl:"hosts"`
HostsInclude []string `hcl:"hosts_include"`
Recorder *recorder.Recorder `hcl:"recorder" toml:"recorder"`
}
// LoadConfig reads the configuration file from disk and parses the configuration structure.
@ -46,9 +53,51 @@ func LoadConfig(name string) (*Config, error) {
if err = hcl.Unmarshal(configData, config); err != nil {
return nil, fmt.Errorf("config: error parsing %s: %w", name, err)
}
if len(config.HostsInclude) > 0 {
if err = loadIncludes(config.HostsInclude, &config.Hosts); err != nil {
return nil, fmt.Errorf("config: error parsing hosts_include: %w", err)
}
}
if len(config.AuthInclude) > 0 {
if err = loadIncludes(config.AuthInclude, &config.Auth); err != nil {
return nil, fmt.Errorf("config: error parsing auth_include: %w", err)
}
}
if len(config.CredentialInclude) > 0 {
if err = loadIncludes(config.CredentialInclude, &config.Credential); err != nil {
return nil, fmt.Errorf("config: error parsing credential_include: %w", err)
}
}
if len(config.RoleInclude) > 0 {
if err = loadIncludes(config.RoleInclude, &config.Role); err != nil {
return nil, fmt.Errorf("config: error parsing role_include: %w", err)
}
}
return config, nil
}
func loadIncludes(patterns []string, value interface{}) (err error) {
for _, pattern := range patterns {
var names []string
if names, err = filepath.Glob(pattern); err != nil {
return fmt.Errorf("error expanding %s: %w", pattern, err)
}
for _, name := range names {
var includeData []byte
if includeData, err = ioutil.ReadFile(name); err != nil { // #nosec
return fmt.Errorf("error reading %s: %w", name, err)
}
if err = hcl.Unmarshal(includeData, value); err != nil {
return fmt.Errorf("error parsing %s: %w", name, err)
}
}
}
return
}
// Logger returns a configured Logger.
func (config *Config) Logger() *logger.Logger {
log := logger.New(os.Stdout)
@ -63,6 +112,9 @@ func (config *Config) Providers() (*core.Providers, error) {
providers = new(core.Providers)
err error
)
if providers.Hosts, err = config.Hosts.Providers(logger.Default); err != nil {
return nil, err
}
if providers.Authentication, err = config.Auth.Providers(logger.Default); err != nil {
return nil, err
}


+ 25
- 0
pkg/core/host.go View File

@ -0,0 +1,25 @@
package core
import "net"
type Host struct {
// Name is the full qualified hostname.
Name string `json:"name"`
// Aliases for this host.
Aliases []string `json:"aliases,omitempty"`
// IPs are the IP addresses.
IPs []net.IP `json:"ips,omitempty"`
}
type HostsProvider interface {
Plugin
// Hosts returns all available hosts.
Hosts() []Host
// MatchHost returns a slice of host that match the glob pattern.
// If limit <= 0, no limit is applied.
MatchHost(pattern string, limit int) []Host
}

+ 7
- 0
pkg/core/runner.go View File

@ -78,6 +78,13 @@ func (r *Runner) startProviders() (err error) {
return
}
r.log.WithField("providers", len(r.Providers.Hosts)).Debug("starting hosts providers")
for _, provider := range r.Providers.Hosts {
if err = r.startProvider(provider); err != nil {
return
}
}
r.log.WithField("providers", len(r.Providers.Authentication)).Debug("starting authentication providers")
for _, provider := range r.Providers.Authentication {
if err = r.startProvider(provider); err != nil {


+ 3
- 0
pkg/core/service.go View File

@ -65,6 +65,9 @@ type AgentTransporter interface {
type Tunnel interface {
net.Conn
// TunnelID is the globally unique identifier.
TunnelID() compact.ID
// User is the authenticated user on this transport.
User() User


+ 16
- 1
pkg/core/user.go View File

@ -1,6 +1,10 @@
package core
import "maze.io/gate/pkg/util/compact"
import (
"os/user"
"maze.io/gate/pkg/util/compact"
)
// User entity.
type User interface {
@ -16,3 +20,14 @@ type User interface {
// Groups is a slice of groups this user is a member of.
Groups() []Group
}
func IsUserNotFound(err error) bool {
switch err.(type) {
case user.UnknownUserError:
return true
case user.UnknownUserIdError:
return true
default:
return false
}
}

+ 31
- 0
pkg/crypto/authority/ca.go View File

@ -0,0 +1,31 @@
package authority
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/ssh"
)
type CertificateAuthority struct {
hostKeySigner ssh.Signer
userKeySigner ssh.Signer
}
func New(hostKey, userKey ssh.Signer) *CertificateAuthority {
return &CertificateAuthority{
hostKeySigner: hostKey,
userKeySigner: userKey,
}
}
func (ca *CertificateAuthority) Sign(certificate *ssh.Certificate) error {
switch certificate.CertType {
case ssh.UserCert:
return certificate.SignCert(rand.Reader, ca.userKeySigner)
case ssh.HostCert:
return certificate.SignCert(rand.Reader, ca.hostKeySigner)
default:
return fmt.Errorf("authority: unsupported certificate type %d", certificate.CertType)
}
}

+ 95
- 0
pkg/crypto/ksm/hsm.go View File

@ -0,0 +1,95 @@
package ksm
import (
"crypto"
"crypto/elliptic"
"github.com/ThalesIgnite/crypto11"
"github.com/mitchellh/mapstructure"
"golang.org/x/crypto/ssh"
"maze.io/gate/pkg/core"
"maze.io/gate/pkg/core/logger"
)
type hsm struct {
// Full path to PKCS#11 library.
Path string
// Token serial number.
TokenSerial string `mapstructure:"serial"`
// Token label.
TokenLabel string `mapstructure:"label"`
// SlotNumber identifies a token to use by the slot containing it.
SlotNumber *int `mapstructure:"slot"`
// User PIN (password).
Pin string
log *logger.Logger
ctx *crypto11.Context
}
func (hsm) Component() string {
return "hsm"
}
func (backend *hsm) Setup(config map[string]interface{}, log *logger.Logger) (err error) {
backend.log = log.WithField(logger.Component, backend.Component())
if err = mapstructure.Decode(config, backend); err != nil {
return err
}
if backend.ctx, err = crypto11.Configure(&crypto11.Config{
Path: backend.Path,
TokenSerial: backend.TokenSerial,
TokenLabel: backend.TokenLabel,
SlotNumber: backend.SlotNumber,
Pin: backend.Pin,
}); err != nil {
return err
}
return nil
}
func (hsm) Start(_ *core.Runner) error {
return nil
}
func (backend *hsm) NewSigner(name string, keyAlgorithm string, bits int) (ssh.Signer, error) {
var (
id = []byte(name)
signer crypto.Signer
err error
)
switch keyAlgorithm {
case ssh.KeyAlgoRSA:
signer, err = backend.ctx.GenerateRSAKeyPairWithLabel(id, id, bits)
case ssh.KeyAlgoECDSA256:
signer, err = backend.ctx.GenerateECDSAKeyPairWithLabel(id, id, elliptic.P256())
case ssh.KeyAlgoECDSA384:
signer, err = backend.ctx.GenerateECDSAKeyPairWithLabel(id, id, elliptic.P384())
case ssh.KeyAlgoECDSA521:
signer, err = backend.ctx.GenerateECDSAKeyPairWithLabel(id, id, elliptic.P521())
}
if err != nil {
return nil, err
}
return ssh.NewSignerFromSigner(signer)
}
func (backend *hsm) GetSigner(name string) (ssh.Signer, error) {
s, err := backend.ctx.FindKeyPair(nil, []byte(name))
if err != nil {
return nil, err
}
return ssh.NewSignerFromSigner(s)
}
var (
_ core.Plugin = (*hsm)(nil)
_ Backend = (*hsm)(nil)
)

+ 192
- 0
pkg/crypto/ksm/ksm.go View File

@ -0,0 +1,192 @@
package ksm
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/mitchellh/mapstructure"
"golang.org/x/crypto/ssh"
"maze.io/gate/pkg/core"
"maze.io/gate/pkg/core/logger"
)
// Backend can store SSH private keys.
type Backend interface {
// NewSigner creates a new signer.
NewSigner(name string, keyAlgorithm string, bits int) (ssh.Signer, error)
// GetSigner gets a signer by name.
GetSigner(name string) (ssh.Signer, error)
}
type disk struct {
Path string
log *logger.Logger
random io.Reader
}
func (disk) Component() string {
return "ksm.disk"
}
func (backend *disk) Setup(config map[string]interface{}, log *logger.Logger) error {
backend.log = log.WithField(logger.Component, backend.Component())
backend.random = rand.Reader
if err := mapstructure.Decode(config, backend); err != nil {
return err
}
if !filepath.IsAbs(backend.Path) {
var err error
if backend.Path, err = filepath.Abs(backend.Path); err != nil {
return fmt.Errorf("ksm: error resolving %s: %w", backend.Path, err)
}
}
i, err := os.Stat(backend.Path)
if err != nil {
return err
} else if !i.IsDir() {
return fmt.Errorf("ksm: %s is not a directory", backend.Path)
} else if i.Mode()&007 != 0 {
return fmt.Errorf("ksm: %s is other accessible (mode %s)", backend.Path, i.Mode())
}
return nil
}
func (backend *disk) Start(_ *core.Runner) error {
return nil
}
func (backend *disk) NewSigner(name string, keyAlgorithm string, bits int) (ssh.Signer, error) {
algorithm := keyAlgorithm
if !strings.HasPrefix(algorithm, "ssh-") {
algorithm = "ssh-" + algorithm
}
switch algorithm {
case ssh.KeyAlgoRSA:
return backend.newRSASigner(name, bits)
case ssh.KeyAlgoECDSA256:
return backend.newECDSASigner(name, 256)
case ssh.KeyAlgoECDSA384:
return backend.newECDSASigner(name, 384)
case ssh.KeyAlgoECDSA521:
return backend.newECDSASigner(name, 521)
case ssh.KeyAlgoED25519:
return backend.newEd25519Signer(name)
default:
return nil, fmt.Errorf("ksm: unsupported key algorithm %q specified", keyAlgorithm)
}
}
func (backend *disk) newRSASigner(name string, bits int) (ssh.Signer, error) {
if bits < 2048 {
bits = 2048
}
k, err := rsa.GenerateKey(backend.random, bits)
if err != nil {
return nil, err
} else if err = k.Validate(); err != nil {
return nil, err
}
if err = backend.store(name, "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(k)); err != nil {
return nil, err
}
return ssh.NewSignerFromKey(k)
}
func (backend *disk) newECDSASigner(name string, bits int) (ssh.Signer, error) {
var curve elliptic.Curve
switch bits {
case 0, 256:
curve = elliptic.P256()
case 384:
curve = elliptic.P384()
case 521:
curve = elliptic.P521()
default:
return nil, fmt.Errorf("ksm: unsupported ECDSA key size %d-bits (valid are 256, 834 or 521)", bits)
}
k, err := ecdsa.GenerateKey(curve, backend.random)
if err != nil {
return nil, err
}
derBytes, err := x509.MarshalECPrivateKey(k)
if err != nil {
return nil, err
}
if err = backend.store(name, "EC PRIVATE KEY", derBytes); err != nil {
return nil, err
}
return ssh.NewSignerFromKey(k)
}
func (backend *disk) newEd25519Signer(name string) (ssh.Signer, error) {
_, k, err := ed25519.GenerateKey(backend.random)
if err != nil {
return nil, err
}
if err = backend.store(name, "OPENSSH PRIVATE KEY", marshalED25519PrivateKey(k)); err != nil {
return nil, err
}
return ssh.NewSignerFromKey(k)
}
func (backend *disk) store(name string, pemType string, derBytes []byte) error {
f, err := os.OpenFile(filepath.Join(backend.Path, name), os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
if _, err = f.Write(pem.EncodeToMemory(&pem.Block{
Type: pemType,
Bytes: derBytes,
})); err != nil {
_ = f.Close()
return err
}
if err = f.Close(); err != nil {
_ = os.Remove(filepath.Join(backend.Path, name))
return err
}
return nil
}
func (backend *disk) GetSigner(name string) (ssh.Signer, error) {
b, err := ioutil.ReadFile(filepath.Join(backend.Path, name))
if err != nil {
return nil, err
}
return ssh.ParsePrivateKey(b)
}
var (
_ core.Plugin = (*disk)(nil)
_ Backend = (*disk)(nil)
)

+ 88
- 0
pkg/crypto/ksm/util.go View File

@ -0,0 +1,88 @@
package ksm
import (
"math/rand"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh"
)
/* Writes ed25519 private keys into the new OpenSSH private key format.
I have no idea why this isn't implemented anywhere yet, you can do seemingly
everything except write it to disk in the OpenSSH private key format. */
func marshalED25519PrivateKey(key ed25519.PrivateKey) []byte {
// Add our key header (followed by a null byte)
magic := append([]byte("openssh-key-v1"), 0)
var w struct {
CipherName string
KdfName string
KdfOpts string
NumKeys uint32
PubKey []byte
PrivKeyBlock []byte
}
// Fill out the private key fields
pk1 := struct {
Check1 uint32
Check2 uint32
Keytype string
Pub []byte
Priv []byte
Comment string
Pad []byte `ssh:"rest"`
}{}
// Set our check ints
ci := rand.Uint32()
pk1.Check1 = ci
pk1.Check2 = ci
// Set our key type
pk1.Keytype = ssh.KeyAlgoED25519
// Add the pubkey to the optionally-encrypted block
pk, ok := key.Public().(ed25519.PublicKey)
if !ok {
//fmt.Fprintln(os.Stderr, "ed25519.PublicKey type assertion failed on an ed25519 public key. This should never ever happen.")
return nil
}
pubKey := []byte(pk)
pk1.Pub = pubKey
// Add our private key
pk1.Priv = []byte(key)
// Might be useful to put something in here at some point
pk1.Comment = ""
// Add some padding to match the encryption block size within PrivKeyBlock (without Pad field)
// 8 doesn't match the documentation, but that's what ssh-keygen uses for unencrypted keys. *shrug*
bs := 8
blockLen := len(ssh.Marshal(pk1))
padLen := (bs - (blockLen % bs)) % bs
pk1.Pad = make([]byte, padLen)
// Padding is a sequence of bytes like: 1, 2, 3...
for i := 0; i < padLen; i++ {
pk1.Pad[i] = byte(i + 1)
}
// Generate the pubkey prefix "\0\0\0\nssh-ed25519\0\0\0 "
prefix := []byte{0x0, 0x0, 0x0, 0x0b}
prefix = append(prefix, []byte(ssh.KeyAlgoED25519)...)
prefix = append(prefix, []byte{0x0, 0x0, 0x0, 0x20}...)
// Only going to support unencrypted keys for now
w.CipherName = "none"
w.KdfName = "none"
w.KdfOpts = ""
w.NumKeys = 1
w.PubKey = append(prefix, pubKey...)
w.PrivKeyBlock = ssh.Marshal(pk1)
magic = append(magic, ssh.Marshal(w)...)
return magic
}

+ 12
- 0
pkg/db/db.go View File

@ -0,0 +1,12 @@
package db
type Backend interface {
// Delete a setting by name.
Delete(key string) (ok bool, err error)
// Get a setting by name, passing the value back.
Get(key string, value interface{}) error
// Set a setting by name.
Set(key string, value interface{}) error
}

+ 121
- 0
pkg/db/sqlite.go View File

@ -0,0 +1,121 @@
package db