Skip to content

Commit

Permalink
Merge branch 'main' into opkssh3
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanHeilman authored Sep 9, 2024
2 parents ad02d32 + 5b4b242 commit c72beec
Show file tree
Hide file tree
Showing 13 changed files with 104 additions and 88 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
paths-ignore:
- '**/README.md'
workflow_dispatch:
jobs:
build-linux:
strategy:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
paths-ignore:
- '**/README.md'
workflow_dispatch:
jobs:
build-linux:
strategy:
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
<img src="resources/figures/ssh3.png" style="display: block; width: 60%">
</div>

> [!NOTE]
> SSH3 is probably going to change its name. It is still the SSH Connection Protocol (RFC4254) running on top of HTTP/3 Extended connect, but the required changes are heavy and
> too distant from the philosophy of popular SSH implementations to be considered for integration. The [specification draft](https://datatracker.ietf.org/doc/draft-michel-remote-terminal-http3/) has already been renamed ("Remote Terminals over HTTP/3"),
> but we need some time to come up with a nice permanent name.
# SSH3: faster and rich secure shell using HTTP/3
SSH3 is a complete revisit of the SSH
protocol, mapping its semantics on top of the HTTP mechanisms. It comes from our research work and we (researchers) recently proposed it as an [Internet-Draft](https://www.ietf.org/how/ids/) ([draft-michel-ssh3-00](https://www.ietf.org/archive/id/draft-michel-ssh3-00.html)).
protocol, mapping its semantics on top of the HTTP mechanisms. It comes from our research work and we (researchers) recently proposed it as an [Internet-Draft](https://www.ietf.org/how/ids/) ([draft-michel-remote-terminal-http3-00](https://datatracker.ietf.org/doc/draft-michel-remote-terminal-http3/)).

In a nutshell, SSH3 uses [QUIC](https://datatracker.ietf.org/doc/html/rfc9000)+[TLS1.3](https://datatracker.ietf.org/doc/html/rfc8446) for
secure channel establishment and the [HTTP Authorization](https://www.rfc-editor.org/rfc/rfc9110.html#name-authorization) mechanisms for user authentication.
Expand Down
14 changes: 9 additions & 5 deletions auth/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

/////////////////////////////////////
// Server auth plugins //
// Server auth plugins //
/////////////////////////////////////

// In ssh3, authorized_keys are replaced by authorized_identities where a use can specify classical
Expand All @@ -27,11 +27,15 @@ type RequestIdentityVerifier interface {
type ServerAuthPlugin func(username string, identityStr string) (RequestIdentityVerifier, error)

/////////////////////////////////////
// Client auth plugins //
// Client auth plugins //
/////////////////////////////////////

// Updates `request` with the correct authentication material so that an SSH3 conversation
// can be established by performing the request
// returns all the suitable authentication methods to be tried against the server in the form
// of a slice of ClientAuthMethod. Every ClientAuthMethod will have the opportunity to prepare
// an HTTP request with authentication material to startup an SSH3 conversation. For instance,
// for pubkey authentication using the private key files on the filesystem, the
// GetClientAuthMethodsFunc can return a slice containing one ClientAuthMethod for
// each private key file it wants to try.
// if no SSH agent socket if found, sshAgent is nil
type GetClientAuthMethodsFunc func(request *http.Request, sshAgent agent.ExtendedAgent, clientConfig *client_config.Config, roundTripper *http3.RoundTripper) ([]ClientAuthMethod, error)

Expand All @@ -52,7 +56,7 @@ type ClientAuthPlugin struct {
// A plugin can define one or more new SSH3 config options.
// A new option is defined by providing a dedicated option parser.
// The key in PluginOptions must be a unique name for each option
// and must not confict with any existing option
// and must not conflict with any existing option
// (good practice: "<your_repo_name>[-<option_name>]")
PluginOptions map[client_config.OptionName]client_config.OptionParser

Expand Down
14 changes: 3 additions & 11 deletions auth/oidc/openid_connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package oidc
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"net"
"net/http"
Expand Down Expand Up @@ -37,20 +36,13 @@ func Connect(ctx context.Context, oidcConfig *OIDCConfig, issuerURL string, doPK

providerEndpoint := provider.Endpoint()

randomSecretUrlBytes := [64]byte{}
_, err = rand.Read(randomSecretUrlBytes[:])
if err != nil {
return "", err
}

randomSecretUrl := hex.EncodeToString(randomSecretUrlBytes[:])

listener, err := net.Listen("tcp", ":0")
if err != nil {
panic(err)
}

path := fmt.Sprintf("/ssh/%s", randomSecretUrl)

path := "/ssh"
listeningPort := listener.Addr().(*net.TCPAddr).Port

secretUrl := fmt.Sprintf("http://localhost:%d%s", listeningPort, path)
Expand All @@ -74,7 +66,7 @@ func Connect(ctx context.Context, oidcConfig *OIDCConfig, issuerURL string, doPK
return "", fmt.Errorf("error when generating random verifier: %s", err.Error())
}

verifier := string(challengeVerifierBytes[:])
verifier := oauth2.GenerateVerifier()

tokenChannel := make(chan string)
mux := http.NewServeMux()
Expand Down
36 changes: 18 additions & 18 deletions auth/plugins/pubkey_authentication/client/privkey_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ func init() {

const PRIVKEY_OPTION_NAME = "github.com/francoismichel/ssh3-privkey_auth"

// impements client-side pubkey-based authentication

// implements client-side pubkey-based authentication

type PrivkeyAuthOption struct {
filenames []string
Expand Down Expand Up @@ -132,6 +133,8 @@ func (m *PrivkeyFileAuthMethod) PrepareRequestForAuth(request *http.Request, ssh
filePubkey, _, _, _, err := ssh.ParseAuthorizedKey(pubkeyBytes)
if err == nil {
pubkey = filePubkey
} else {
log.Warn().Msgf("an error happened when trying to parse the %s.pub file for agent-based authentication: %s", m.Filename(), err)
}
}
}
Expand All @@ -145,7 +148,6 @@ func (m *PrivkeyFileAuthMethod) PrepareRequestForAuth(request *http.Request, ssh
}
}
// now, try to see of the agent manages this key
foundAgentKey := false
if pubkey != nil {
for _, agentKey := range agentKeys {
if bytes.Equal(agentKey.Marshal(), pubkey.Marshal()) {
Expand All @@ -158,22 +160,20 @@ func (m *PrivkeyFileAuthMethod) PrepareRequestForAuth(request *http.Request, ssh
}

// key not handled by agent, let's try to decrypt it ourselves
if !foundAgentKey {
fmt.Printf("passphrase for private key stored in %s:", m.Filename())
var passphraseBytes []byte
passphraseBytes, err = term.ReadPassword(int(syscall.Stdin))
fmt.Println()
if err != nil {
log.Error().Msgf("could not get passphrase: %s", err)
return err
}
passphrase := string(passphraseBytes)
m.passphrase = &passphrase
jwtBearerKey, signingMethod, err = m.getCryptoMaterial()
if err != nil {
log.Error().Msgf("could not load private key: %s", err)
return err
}
fmt.Printf("passphrase for private key stored in %s:", m.Filename())
var passphraseBytes []byte
passphraseBytes, err = term.ReadPassword(int(syscall.Stdin))
fmt.Println()
if err != nil {
log.Error().Msgf("could not get passphrase: %s", err)
return err
}
passphrase := string(passphraseBytes)
m.passphrase = &passphrase
jwtBearerKey, signingMethod, err = m.getCryptoMaterial()
if err != nil {
log.Error().Msgf("could not load private key: %s", err)
return err
}
} else if err != nil {
log.Warn().Msgf("Could not load private key: %s", err)
Expand Down
38 changes: 25 additions & 13 deletions auth/plugins/pubkey_authentication/client/pubkey_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,25 @@ var _ config.CLIOptionParser = &PrivkeyOptionParser{}

// agentSigningMethod implements jwt.SigningMethod to use the SSH agent with the jwt lib
type agentSigningMethod struct {
Agent agent.ExtendedAgent
Key ssh.PublicKey
agent agent.ExtendedAgent
key ssh.PublicKey
alg string
}

func NewAgentSigningMethod(agent agent.ExtendedAgent, key ssh.PublicKey) (*agentSigningMethod, error) {
ret := &agentSigningMethod{
key: key,
agent: agent,
}
switch key.Type() {
case "ssh-rsa":
ret.alg = "RS256"
case "ssh-ed25519":
ret.alg = "EdDSA"
default:
return nil, fmt.Errorf("unsupported key type for agent signing method")
}
return ret, nil
}

func (m *agentSigningMethod) Verify(signingString string, sig []byte, key interface{}) error {
Expand All @@ -85,21 +102,16 @@ func (m *agentSigningMethod) Sign(signingString string, key interface{}) ([]byte
if !ok {
return nil, fmt.Errorf("bad key type: %T instead of ssh.PublicKey", pk)
}
signature, err := m.Agent.SignWithFlags(pk, []byte(signingString), agent.SignatureFlagRsaSha256)

signature, err := m.agent.SignWithFlags(pk, []byte(signingString), agent.SignatureFlagRsaSha256)
if err != nil {
return nil, err
}
return signature.Blob, nil
}

func (m *agentSigningMethod) Alg() string {
switch m.Key.Type() {
case "ssh-rsa":
return "RS256"
case "ssh-ed25519":
return "EdDSA"
}
return ""
return m.alg
}

type PubkeyAuthMethod struct {
Expand All @@ -114,9 +126,9 @@ func NewPubkeyAuthMethod(pubkey *agent.Key) *PubkeyAuthMethod {
func (m *PubkeyAuthMethod) PrepareRequestForAuth(request *http.Request, sshAgent agent.ExtendedAgent, roundTripper *http3.RoundTripper, username string, conversation *ssh3.Conversation) error {
log.Debug().Msgf("try agent-based pubkey auth using pubkey %s", m.Key.String())

signingMethod := &agentSigningMethod{
Agent: sshAgent,
Key: m.Key,
signingMethod, err := NewAgentSigningMethod(sshAgent, m.Key)
if err != nil {
return err
}

bearerToken, err := ssh3.BuildJWTBearerToken(signingMethod, m.Key, username, conversation)
Expand Down
1 change: 0 additions & 1 deletion cmd/plugin_endpoint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
_ "github.com/francoismichel/ssh3/auth/plugins/openpubkey/client"
_ "github.com/francoismichel/ssh3/auth/plugins/pubkey_authentication/client"

cmd "github.com/francoismichel/ssh3/cmd"
)

Expand Down
5 changes: 2 additions & 3 deletions cmd/ssh3-server/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package main

import (
"os"

"github.com/francoismichel/ssh3/cmd"
"os"

// authentication plugins
_ "github.com/francoismichel/ssh3/auth/plugins/openpubkey/server"
_ "github.com/francoismichel/ssh3/auth/plugins/pubkey_authentication/server"
_ "github.com/francoismichel/ssh3/auth/plugins/openpubkey/server"
)

func main() {
Expand Down
8 changes: 6 additions & 2 deletions cmd/ssh3.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ func ClientMain() int {
oidcConfigFileName := flag.String("oidc-config", "", "OpenID Connect json config file containing the \"client_id\" and \"client_secret\" fields needed for most identity providers")
verbose := flag.Bool("v", false, "if set, enable verbose mode")
displayVersion := flag.Bool("version", false, "if set, displays the software version on standard output and exit")
doPKCE := flag.Bool("do-pkce", false, "if set perform PKCE challenge-response with oidc")
noPKCE := flag.Bool("no-pkce", false, "if set perform PKCE challenge-response with oidc")
forwardSSHAgent := flag.Bool("forward-agent", false, "if set, forwards ssh agent to be used with sshv2 connections on the remote host")
forwardUDP := flag.String("forward-udp", "", "if set, take a localport/remoteip@remoteport forwarding localhost@localport towards remoteip@remoteport")
forwardTCP := flag.String("forward-tcp", "", "if set, take a localport/remoteip@remoteport forwarding localhost@localport towards remoteip@remoteport")
Expand Down Expand Up @@ -430,6 +430,10 @@ func ClientMain() int {

log.Debug().Msgf("version %s", ssh3.GetCurrentSoftwareVersion())

if *noPKCE {
log.Warn().Msgf("Disabling PKCE is considered insecure to machine-in-the-middle attacks. Consider enabling PKCE by default!")
}

knownHostsPath := path.Join(ssh3Dir, "known_hosts")
knownHosts, skippedLines, err := ssh3.ParseKnownHosts(knownHostsPath)
if len(skippedLines) != 0 {
Expand Down Expand Up @@ -579,7 +583,7 @@ func ClientMain() int {
for _, issuerConfig := range oidcConfig {
if *issuerUrl == issuerConfig.IssuerUrl {
log.Debug().Msgf("found issuer %s matching the issuer specified in the command-line", issuerConfig.IssuerUrl)
cliAuthMethods = append(cliAuthMethods, ssh3.NewOidcAuthMethod(*doPKCE, issuerConfig))
cliAuthMethods = append(cliAuthMethods, ssh3.NewOidcAuthMethod(!*noPKCE, issuerConfig))
} else {
log.Debug().Msgf("issuer %s does not match issuer URL %s specified in the command-line", issuerConfig.IssuerUrl, *issuerUrl)
}
Expand Down
5 changes: 2 additions & 3 deletions cmd/ssh3/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package main

import (
"os"

"github.com/francoismichel/ssh3/cmd"
"os"

// authentication plugins
_ "github.com/francoismichel/ssh3/auth/plugins/openpubkey/client"
_ "github.com/francoismichel/ssh3/auth/plugins/pubkey_authentication/client"
_ "github.com/francoismichel/ssh3/auth/plugins/openpubkey/client"
)

func main() {
Expand Down
59 changes: 31 additions & 28 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
filippo.io/bigmod v0.0.3 h1:qmdCFHmEMS+PRwzrW6eUrgA4Q3T8D6bRcjsypDMtWHM=
filippo.io/bigmod v0.0.3/go.mod h1:WxGvOYE0OUaBC2N112Dflb3CjOnMBuNRA2UWZc2UbPE=
github.com/awnumar/memcall v0.1.2 h1:7gOfDTL+BJ6nnbtAp9+HQzUFjtP1hEseRQq8eP055QY=
github.com/awnumar/memcall v0.1.2/go.mod h1:S911igBPR9CThzd/hYQQmTc9SWNu3ZHIlCGaWsWsoJo=
github.com/awnumar/memguard v0.22.3 h1:b4sgUXtbUjhrGELPbuC62wU+BsPQy+8lkWed9Z+pj0Y=
github.com/awnumar/memguard v0.22.3/go.mod h1:mmGunnffnLHlxE5rRgQc3j+uwPZ27eYb61ccr8Clz2Y=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand Down Expand Up @@ -122,10 +114,9 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
Expand Down Expand Up @@ -156,19 +147,26 @@ go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -178,11 +176,16 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
Expand Down
Loading

0 comments on commit c72beec

Please sign in to comment.