mirror of
https://github.com/xor-gate/go-socks5-ssh-proxy
synced 2026-03-23 06:16:35 +01:00
Initial working version
This commit is contained in:
32
vendor/github.com/xor-gate/sshfp/.gometalinter.conf
generated
vendored
Normal file
32
vendor/github.com/xor-gate/sshfp/.gometalinter.conf
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"Vendor": true,
|
||||
"LineLength": 120,
|
||||
"Deadline": "5m",
|
||||
"Exclude" : [
|
||||
"examples",
|
||||
".pb.go"
|
||||
],
|
||||
"Enable": [
|
||||
"maligned",
|
||||
"deadcode",
|
||||
"gosec",
|
||||
"goconst",
|
||||
"gocyclo",
|
||||
"gofmt",
|
||||
"golint",
|
||||
"ineffassign",
|
||||
"interfacer",
|
||||
"lll",
|
||||
"misspell",
|
||||
"structcheck",
|
||||
"unconvert",
|
||||
"varcheck",
|
||||
"vet",
|
||||
"vetshadow"],
|
||||
"Severity": {
|
||||
"golint" : "error",
|
||||
"gotype" : "error",
|
||||
"vet" : "error",
|
||||
"vetshadow" : "error"
|
||||
}
|
||||
}
|
||||
36
vendor/github.com/xor-gate/sshfp/.travis.sh
generated
vendored
Normal file
36
vendor/github.com/xor-gate/sshfp/.travis.sh
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
GREP_EXCLUDES="vendor\|examples"
|
||||
LINT_CMD="gometalinter --config .gometalinter.conf"
|
||||
|
||||
echo "--- go env"
|
||||
go env
|
||||
|
||||
#echo "--- lint (gometalinter)"
|
||||
#go get -v -u github.com/alecthomas/gometalinter
|
||||
#gometalinter --install
|
||||
#${LINT_CMD} ./...
|
||||
|
||||
#echo "--- create build folder ./build"
|
||||
#mkdir -p build
|
||||
|
||||
#echo "--- build and lint examples/*.go"
|
||||
#for e in $(ls -1 examples/*.go); do
|
||||
# filename=$(basename $e)
|
||||
# filename="${filename%.*}"
|
||||
# echo "* $filename"
|
||||
# ${LINT_CMD} $e
|
||||
# go build -o build/$filename $e
|
||||
#done
|
||||
|
||||
echo "--- go test (with race detector and coverage)"
|
||||
echo "" > coverage.txt
|
||||
|
||||
for d in $(go list ./... | grep -v ${GREP_EXCLUDES}); do
|
||||
go test -race -coverprofile=profile.out -covermode=atomic $d
|
||||
if [ -f profile.out ]; then
|
||||
cat profile.out >> coverage.txt
|
||||
rm profile.out
|
||||
fi
|
||||
done
|
||||
24
vendor/github.com/xor-gate/sshfp/.travis.yml
generated
vendored
Normal file
24
vendor/github.com/xor-gate/sshfp/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
sudo: false
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
language: go
|
||||
go:
|
||||
- tip
|
||||
- "1.14"
|
||||
- "1.13"
|
||||
- "1.12"
|
||||
# Latest golang.org/x/sys/unix requires go 1.12
|
||||
# Prior to go 1.11 will not work because we need go modules
|
||||
# Prior to go 1.9 will not work due to github.com/miekg/dns dependency
|
||||
|
||||
script:
|
||||
- ./.travis.sh
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
21
vendor/github.com/xor-gate/sshfp/LICENSE
generated
vendored
Normal file
21
vendor/github.com/xor-gate/sshfp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Jerry Jacobs <jerry.jacobs@xor-gate.org>
|
||||
|
||||
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.
|
||||
33
vendor/github.com/xor-gate/sshfp/Makefile
generated
vendored
Normal file
33
vendor/github.com/xor-gate/sshfp/Makefile
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
export CGO_ENABLED=0
|
||||
export GOPATH?=$(shell go env GOPATH)
|
||||
export DESTDIR?=$(GOPATH)/bin
|
||||
export GOBIN?=$(DESTDIR)
|
||||
|
||||
all: build
|
||||
ci: env test
|
||||
|
||||
install:
|
||||
go get ./...
|
||||
|
||||
env:
|
||||
go env
|
||||
echo "---"
|
||||
|
||||
dep:
|
||||
go get -u ./
|
||||
|
||||
build:
|
||||
go build
|
||||
go install github.com/xor-gate/sshfp/cmd/sshfp
|
||||
|
||||
test:
|
||||
go test -v $(shell go list ./... | grep -v '^vendor\/')
|
||||
|
||||
lint:
|
||||
gometalinter --config .gometalinter.conf
|
||||
|
||||
clean:
|
||||
#rm -Rf $(TMPDIR)/debpkg*
|
||||
|
||||
fmt:
|
||||
gofmt -s -w .
|
||||
40
vendor/github.com/xor-gate/sshfp/README.md
generated
vendored
Normal file
40
vendor/github.com/xor-gate/sshfp/README.md
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
# SSHFP resolver for Golang
|
||||
|
||||
[![License][License-Image]][License-Url]
|
||||
[![Gopkg][Gopkg-Image]][Gopkg-Url]
|
||||
[![ReportCard][ReportCard-Image]][ReportCard-Url]
|
||||
[![Build][Build-Status-Image]][Build-Status-Url]
|
||||
[![Coverage][Coverage-Image]][Coverage-Url]
|
||||
|
||||
**NOTE: Package is functional but not production ready! It requires at least Golang 1.12 with Go modules**
|
||||
|
||||
The sshfp project is a Golang implementation of SSH fingerprints stored in SSHFP DNS records which can be used together
|
||||
with [golang.org/x/crypto/ssh.Client](https://godoc.org/golang.org/x/crypto/ssh#Client) using a custom
|
||||
[HostKeyCallback](https://godoc.org/github.com/xor-gate/sshfp#Resolver.HostKeyCallback) with modular caching. Learn more about the DNS SSHFP record type on [Wikipedia](https://en.wikipedia.org/wiki/SSHFP_record).
|
||||
|
||||
At least go 1.12 is required
|
||||
|
||||
This package is based on the awesome Golang DNS package from [github.com/miekg/dns](https://github.com/miekg/dns).
|
||||
|
||||
And implements at least the following RFCs:
|
||||
|
||||
* https://www.ietf.org/rfc/rfc1035.txt
|
||||
* https://tools.ietf.org/rfc/rfc6594.txt
|
||||
* https://www.ietf.org/rfc/rfc4255.txt
|
||||
* https://tools.ietf.org/html/rfc7479
|
||||
|
||||
# LICENSE
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[License-Url]: http://opensource.org/licenses/MIT
|
||||
[License-Image]: https://img.shields.io/npm/l/express.svg
|
||||
[Stability-Status-Image]: http://badges.github.io/stability-badges/dist/experimental.svg
|
||||
[Build-Status-Url]: http://travis-ci.org/xor-gate/sshfp
|
||||
[Build-Status-Image]: https://travis-ci.org/xor-gate/sshfp.svg?branch=develop
|
||||
[Gopkg-Url]: https://pkg.go.dev/github.com/xor-gate/sshfp?tab=doc
|
||||
[Gopkg-Image]: https://img.shields.io/badge/gopkg-documentation-blue
|
||||
[ReportCard-Url]: http://goreportcard.com/report/xor-gate/sshfp
|
||||
[ReportCard-Image]: https://goreportcard.com/badge/github.com/xor-gate/sshfp
|
||||
[Coverage-Url]: https://codecov.io/gh/xor-gate/sshfp
|
||||
[Coverage-image]: https://codecov.io/gh/xor-gate/sshfp/branch/develop/graph/badge.svg
|
||||
61
vendor/github.com/xor-gate/sshfp/cache.go
generated
vendored
Normal file
61
vendor/github.com/xor-gate/sshfp/cache.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2018 sshfp authors. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sshfp
|
||||
|
||||
// Cache for DNS SSHFP entries
|
||||
type Cache interface {
|
||||
Add(e ...*Entry) error
|
||||
Get(hostname string, algo ...Algorithm) (Entries, bool)
|
||||
Remove(e *Entry) error
|
||||
}
|
||||
|
||||
// MemoryCache is a in-memory cache
|
||||
type MemoryCache struct {
|
||||
c map[string]Entries
|
||||
}
|
||||
|
||||
// NewMemoryCache creates a new in-memory cache
|
||||
func NewMemoryCache() (*MemoryCache, error) {
|
||||
return &MemoryCache{c: make(map[string]Entries)}, nil
|
||||
}
|
||||
|
||||
func (mc *MemoryCache) add(hostname string, e *Entry) {
|
||||
entries := mc.c[hostname]
|
||||
if len(entries) == 0 {
|
||||
entries = Entries{}
|
||||
mc.c[hostname] = entries
|
||||
}
|
||||
mc.c[hostname] = append(mc.c[hostname], e)
|
||||
}
|
||||
|
||||
// Add entry to the cache
|
||||
func (mc *MemoryCache) Add(e ...*Entry) error {
|
||||
for _, entry := range e {
|
||||
mc.add(entry.Hostname, entry)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get entries from the cache
|
||||
func (mc *MemoryCache) Get(hostname string, algo ...Algorithm) (Entries, bool) {
|
||||
entries, ok := mc.c[hostname]
|
||||
if len(algo) == 1 {
|
||||
algorithm := uint8(algo[0])
|
||||
fentries := Entries{}
|
||||
for _, entry := range entries {
|
||||
if entry.Algorithm != algorithm {
|
||||
continue
|
||||
}
|
||||
fentries = append(fentries, entry)
|
||||
}
|
||||
entries = fentries
|
||||
}
|
||||
return entries, ok
|
||||
}
|
||||
|
||||
// Remove entry from the cache
|
||||
func (mc *MemoryCache) Remove(e *Entry) error {
|
||||
return nil
|
||||
}
|
||||
15
vendor/github.com/xor-gate/sshfp/doc.go
generated
vendored
Normal file
15
vendor/github.com/xor-gate/sshfp/doc.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// Package sshfp implements a ssh.HostKeyCallback for resolving SSH host key fingerprints using DNS
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// The most basic resolver is created as follows (without error checking):
|
||||
//
|
||||
// r := sshfp.NewResolver(sshfp.WithDNSClientConfigFromFile("/etc/resolv.conf"))
|
||||
//
|
||||
// sshClientConfig := &ssh.ClientConfig{
|
||||
// HostKeyCallback: r.HostKeyCallback,
|
||||
// }
|
||||
//
|
||||
// c, err := ssh.Dial("tcp", "remote.example.org:22", sshClientConfig)
|
||||
// ... Check error and do something with the SSHFP validated connected client
|
||||
package sshfp
|
||||
64
vendor/github.com/xor-gate/sshfp/entry.go
generated
vendored
Normal file
64
vendor/github.com/xor-gate/sshfp/entry.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package sshfp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// Entry wraps a DNS SSHFP entry used for caching
|
||||
type Entry struct {
|
||||
*dns.SSHFP
|
||||
ExpiresAt time.Time
|
||||
Hostname string
|
||||
Fingerprint []byte
|
||||
}
|
||||
|
||||
// Entries for a single Hostname
|
||||
type Entries []*Entry
|
||||
|
||||
// Validate checks if the SSH public key is valid
|
||||
func (e *Entry) Validate(pubKey ssh.PublicKey) bool {
|
||||
if e.Fingerprint == nil {
|
||||
return false
|
||||
}
|
||||
fp := sha256.Sum256(pubKey.Marshal())
|
||||
return bytes.Equal(e.Fingerprint, fp[:])
|
||||
}
|
||||
|
||||
// TTL calculates the remaining seconds the entry is valid. When ExpiresAt field is zero then it
|
||||
// never expires and returns math.MaxUint32.
|
||||
func (e *Entry) TTL() uint32 {
|
||||
if e.ExpiresAt.IsZero() {
|
||||
return math.MaxUint32
|
||||
}
|
||||
ttl := time.Until(e.ExpiresAt)
|
||||
if ttl < 1 {
|
||||
return 0
|
||||
}
|
||||
return uint32(ttl / time.Second)
|
||||
}
|
||||
|
||||
// IsExpired checks if the entry is expired
|
||||
func (e *Entry) IsExpired() bool {
|
||||
if e.ExpiresAt.IsZero() {
|
||||
return false
|
||||
}
|
||||
return time.Now().After(e.ExpiresAt)
|
||||
}
|
||||
|
||||
// IsValid checks if the entry is valid
|
||||
func (e *Entry) IsValid() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// String creates a human readable presentation of the SSHFP entry
|
||||
// <hostname> <algorithm string> <fingerprint type string>
|
||||
func (e *Entry) String() string {
|
||||
return fmt.Sprintf("%s %s %s", e.Hostname, Algorithm(e.SSHFP.Algorithm), Type(e.SSHFP.Type))
|
||||
}
|
||||
196
vendor/github.com/xor-gate/sshfp/resolver.go
generated
vendored
Normal file
196
vendor/github.com/xor-gate/sshfp/resolver.go
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright 2018 sshfp authors. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sshfp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// Resolver resolves DNS SSHFP records
|
||||
type Resolver struct {
|
||||
c Cache
|
||||
cc *dns.ClientConfig
|
||||
}
|
||||
|
||||
// ResolverOption for Resolver
|
||||
type ResolverOption func(*Resolver) error
|
||||
|
||||
// NewResolver creates a new DNS SSHFP resolver
|
||||
func NewResolver(opts ...ResolverOption) (*Resolver, error) {
|
||||
r := &Resolver{}
|
||||
for _, option := range opts {
|
||||
err := option(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a cache is attached, or else we attach one
|
||||
if r.c == nil {
|
||||
c, err := NewMemoryCache()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.c = c
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// WithCache sets a Cache for the Resolver
|
||||
func WithCache(c Cache) ResolverOption {
|
||||
return func(r *Resolver) error {
|
||||
r.c = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDNSClientConfigFromFile loads a resolv.conf(5) like file
|
||||
func WithDNSClientConfigFromFile(resolvconf string) ResolverOption {
|
||||
return func(r *Resolver) error {
|
||||
cc, err := dns.ClientConfigFromFile(resolvconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.cc = cc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDNSClientConfigFromReader works like WithDNSClientConfigFromFile but takes an io.Reader as argument
|
||||
func WithDNSClientConfigFromReader(resolvconf io.Reader) ResolverOption {
|
||||
return func(r *Resolver) error {
|
||||
cc, err := dns.ClientConfigFromReader(resolvconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.cc = cc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// checkCache checks the cache for a valid fingerprint
|
||||
func (r *Resolver) checkCache(hostname string, key ssh.PublicKey) error {
|
||||
centries, ok := r.c.Get(hostname, AlgorithmFromSSHPublicKey(key))
|
||||
if ok {
|
||||
for _, ce := range centries {
|
||||
if ce.IsExpired() {
|
||||
err := r.c.Remove(ce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ce.Validate(key) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrHostKeyChanged
|
||||
}
|
||||
return ErrNoHostKeyFound
|
||||
}
|
||||
|
||||
// HostKeyCallback with DNS SSHFP entry verification for golang.org/x/crypto/ssh
|
||||
func (r *Resolver) HostKeyCallback(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
hostURL, err := ParseHostname(hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostname = hostURL.Hostname()
|
||||
|
||||
// lookup cache
|
||||
err = r.checkCache(hostname, key)
|
||||
switch err {
|
||||
case ErrNoHostKeyFound:
|
||||
break
|
||||
case nil:
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
// lookup dns
|
||||
entries, err := r.LookupHost(hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// SHA256 checksum of key
|
||||
// TODO should also support other algos
|
||||
keyFpSHA256 := sha256.Sum256(key.Marshal())
|
||||
|
||||
// TODO very naive way to validate, we should match on key type and algo
|
||||
// and don't brute force check
|
||||
for _, entry := range entries {
|
||||
fp, err := hex.DecodeString(entry.FingerPrint)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !bytes.Equal(fp, keyFpSHA256[:]) {
|
||||
continue
|
||||
}
|
||||
|
||||
expiresAt := time.Now().Add(time.Duration(entry.Hdr.Ttl) * time.Second)
|
||||
e := &Entry{
|
||||
SSHFP: entry,
|
||||
ExpiresAt: expiresAt,
|
||||
Hostname: hostname,
|
||||
Fingerprint: fp,
|
||||
}
|
||||
|
||||
return r.c.Add(e)
|
||||
}
|
||||
|
||||
return ErrNoHostKeyFound
|
||||
}
|
||||
|
||||
// LookupHost looks up the given host for DNS SSHFP records
|
||||
func (r *Resolver) LookupHost(hostname string) ([]*dns.SSHFP, error) {
|
||||
if r.cc == nil {
|
||||
return nil, ErrNoDNSServer
|
||||
}
|
||||
if len(r.cc.Servers) == 0 {
|
||||
return nil, ErrNoDNSServer
|
||||
}
|
||||
|
||||
c := new(dns.Client)
|
||||
m := new(dns.Msg)
|
||||
|
||||
m.SetQuestion(dns.Fqdn(hostname), dns.TypeSSHFP)
|
||||
m.RecursionDesired = true
|
||||
|
||||
// TODO error on no DNS servers
|
||||
// TODO loop over r.cc.Servers instead of first entry
|
||||
resp, _, err := c.Exchange(m, net.JoinHostPort(r.cc.Servers[0], r.cc.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.Rcode != dns.RcodeSuccess {
|
||||
return nil, fmt.Errorf("sshfp: DNS error (Rcode %d)", resp.Rcode)
|
||||
}
|
||||
|
||||
var l []*dns.SSHFP
|
||||
|
||||
for _, a := range resp.Answer {
|
||||
sshfp, ok := a.(*dns.SSHFP)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
l = append(l, sshfp)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
148
vendor/github.com/xor-gate/sshfp/sshfp.go
generated
vendored
Normal file
148
vendor/github.com/xor-gate/sshfp/sshfp.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright 2018 sshfp authors. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sshfp
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// ErrHostKeyChanged when the SSH server host key has changed
|
||||
var ErrHostKeyChanged = fmt.Errorf("sshfp: host key changed")
|
||||
|
||||
// ErrNoHostKeyFound when no host key is found in DNS (or cache)
|
||||
var ErrNoHostKeyFound = fmt.Errorf("sshfp: no host key found")
|
||||
|
||||
// ErrNoDNSServer when no DNS servers is available
|
||||
var ErrNoDNSServer = fmt.Errorf("sshfp: no dns server available")
|
||||
|
||||
// ErrInvalidURLScheme when the hostname URL scheme is invalid
|
||||
var ErrInvalidURLScheme = fmt.Errorf("sshfp: invalid url scheme")
|
||||
|
||||
// SSHURLScheme is the URL scheme for SSH hostname urls
|
||||
const SSHURLScheme = "ssh"
|
||||
|
||||
// Algorithm of the host public key
|
||||
type Algorithm uint8
|
||||
|
||||
// golint: nolint
|
||||
const (
|
||||
AlgorithmReserved Algorithm = 0
|
||||
AlgorithmRSA Algorithm = 1
|
||||
AlgorithmDSS Algorithm = 2
|
||||
AlgorithmECDSA Algorithm = 3
|
||||
AlgorithmEd25519 Algorithm = 4
|
||||
)
|
||||
|
||||
// Type of the fingerprint checksum
|
||||
type Type uint8
|
||||
|
||||
// golint: nolint
|
||||
const (
|
||||
TypeReserved Type = 0
|
||||
TypeSHA1 Type = 1
|
||||
TypeSHA256 Type = 2
|
||||
)
|
||||
|
||||
// String gets the algorithm string as defined in RFC. Reserved or unknown algorithms return "AlgorithmReserved"
|
||||
func (a Algorithm) String() string {
|
||||
switch a {
|
||||
case AlgorithmRSA:
|
||||
return "RSA"
|
||||
case AlgorithmDSS:
|
||||
return "DSS"
|
||||
case AlgorithmECDSA:
|
||||
return "ECDSA"
|
||||
case AlgorithmEd25519:
|
||||
return "Ed25519"
|
||||
}
|
||||
return "AlgorithmReserved"
|
||||
}
|
||||
|
||||
// String gets the fingerprint type string as defined in RFC. Reserved or unknown algorithms return "TypeReserved"
|
||||
func (fp Type) String() string {
|
||||
switch fp {
|
||||
case TypeSHA1:
|
||||
return "SHA-1"
|
||||
case TypeSHA256:
|
||||
return "SHA-256"
|
||||
}
|
||||
return "TypeReserved"
|
||||
}
|
||||
|
||||
// ParseZone parses a RFC 1035 zonefile and creates a slice of Entry elements.
|
||||
// This is compatible with the entries the command `ssh-keygen -r <hostname>` generates.
|
||||
func ParseZone(r io.Reader) (Entries, error) {
|
||||
var entries Entries
|
||||
|
||||
tokenC := dns.ParseZone(r, "", "")
|
||||
for token := range tokenC {
|
||||
if token.Error != nil {
|
||||
return nil, token.Error
|
||||
}
|
||||
|
||||
r, ok := token.RR.(*dns.SSHFP)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
fingerprint, err := hex.DecodeString(r.FingerPrint)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
e := &Entry{
|
||||
SSHFP: r,
|
||||
Hostname: strings.Join(dns.SplitDomainName(r.Hdr.Name), "."),
|
||||
Fingerprint: fingerprint,
|
||||
}
|
||||
entries = append(entries, e)
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// ParseHostname parses the hostname into a url.URL it automaticlly appends the SSHURLScheme
|
||||
// when not the hostname is not prefixed with a scheme. The URL scheme must be empty or
|
||||
// "ssh" else the function returns ErrInvalidURLScheme
|
||||
func ParseHostname(hostname string) (*url.URL, error) {
|
||||
// url.Parse needs a scheme so we provide it
|
||||
if !strings.Contains(hostname, "://") {
|
||||
hostname = fmt.Sprintf("ssh://%s", hostname)
|
||||
}
|
||||
|
||||
u, err := url.Parse(hostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case SSHURLScheme:
|
||||
default:
|
||||
return nil, ErrInvalidURLScheme
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// AlgorithmFromSSHPublicKey calculates the Algorithm based on the ssh.PublicKey.Type() (ssh.KeyAlgo* string)
|
||||
func AlgorithmFromSSHPublicKey(pubKey ssh.PublicKey) Algorithm {
|
||||
switch pubKey.Type() {
|
||||
case ssh.KeyAlgoRSA:
|
||||
return AlgorithmRSA
|
||||
case ssh.KeyAlgoDSA:
|
||||
return AlgorithmDSS
|
||||
case ssh.KeyAlgoED25519:
|
||||
return AlgorithmEd25519
|
||||
case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
|
||||
return AlgorithmECDSA
|
||||
}
|
||||
return AlgorithmReserved
|
||||
}
|
||||
Reference in New Issue
Block a user