mirror of
https://github.com/slackhq/nebula.git
synced 2025-11-11 17:43:58 +01:00
Merge remote-tracking branch 'origin/master' into holepunch-remote-allow-list
This commit is contained in:
commit
c87e2cbc70
18
.github/workflows/test.yml
vendored
18
.github/workflows/test.yml
vendored
@ -67,6 +67,24 @@ jobs:
|
|||||||
- name: End 2 end
|
- name: End 2 end
|
||||||
run: make e2evv GOEXPERIMENT=boringcrypto CGO_ENABLED=1
|
run: make e2evv GOEXPERIMENT=boringcrypto CGO_ENABLED=1
|
||||||
|
|
||||||
|
test-linux-pkcs11:
|
||||||
|
name: Build and test on linux with pkcs11
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: make bin-pkcs11
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: make test-pkcs11
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Build and test on ${{ matrix.os }}
|
name: Build and test on ${{ matrix.os }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,5 +13,6 @@
|
|||||||
**.crt
|
**.crt
|
||||||
**.key
|
**.key
|
||||||
**.pem
|
**.pem
|
||||||
|
**.pub
|
||||||
!/examples/quickstart-vagrant/ansible/roles/nebula/files/vagrant-test-ca.key
|
!/examples/quickstart-vagrant/ansible/roles/nebula/files/vagrant-test-ca.key
|
||||||
!/examples/quickstart-vagrant/ansible/roles/nebula/files/vagrant-test-ca.crt
|
!/examples/quickstart-vagrant/ansible/roles/nebula/files/vagrant-test-ca.crt
|
||||||
|
|||||||
23
CHANGELOG.md
23
CHANGELOG.md
@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.9.4] - 2024-09-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Support UDP dialing with gVisor. (#1181)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Make some Nebula state programmatically available via control object. (#1188)
|
||||||
|
- Switch internal representation of IPs to netip, to prepare for IPv6 support
|
||||||
|
in the overlay. (#1173)
|
||||||
|
- Minor build and cleanup changes. (#1171, #1164, #1162)
|
||||||
|
- Various dependency updates. (#1195, #1190, #1174, #1168, #1167, #1161, #1147, #1146)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix a bug on big endian hosts, like mips. (#1194)
|
||||||
|
- Fix a rare panic if a local index collision happens. (#1191)
|
||||||
|
- Fix integer wraparound in the calculation of handshake timeouts on 32-bit targets. (#1185)
|
||||||
|
|
||||||
## [1.9.3] - 2024-06-06
|
## [1.9.3] - 2024-06-06
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
@ -644,7 +664,8 @@ created.)
|
|||||||
|
|
||||||
- Initial public release.
|
- Initial public release.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/slackhq/nebula/compare/v1.9.3...HEAD
|
[Unreleased]: https://github.com/slackhq/nebula/compare/v1.9.4...HEAD
|
||||||
|
[1.9.4]: https://github.com/slackhq/nebula/releases/tag/v1.9.4
|
||||||
[1.9.3]: https://github.com/slackhq/nebula/releases/tag/v1.9.3
|
[1.9.3]: https://github.com/slackhq/nebula/releases/tag/v1.9.3
|
||||||
[1.9.2]: https://github.com/slackhq/nebula/releases/tag/v1.9.2
|
[1.9.2]: https://github.com/slackhq/nebula/releases/tag/v1.9.2
|
||||||
[1.9.1]: https://github.com/slackhq/nebula/releases/tag/v1.9.1
|
[1.9.1]: https://github.com/slackhq/nebula/releases/tag/v1.9.1
|
||||||
|
|||||||
13
Makefile
13
Makefile
@ -40,7 +40,7 @@ ALL_LINUX = linux-amd64 \
|
|||||||
linux-mips64le \
|
linux-mips64le \
|
||||||
linux-mips-softfloat \
|
linux-mips-softfloat \
|
||||||
linux-riscv64 \
|
linux-riscv64 \
|
||||||
linux-loong64
|
linux-loong64
|
||||||
|
|
||||||
ALL_FREEBSD = freebsd-amd64 \
|
ALL_FREEBSD = freebsd-amd64 \
|
||||||
freebsd-arm64
|
freebsd-arm64
|
||||||
@ -63,7 +63,7 @@ ALL = $(ALL_LINUX) \
|
|||||||
e2e:
|
e2e:
|
||||||
$(TEST_ENV) go test -tags=e2e_testing -count=1 $(TEST_FLAGS) ./e2e
|
$(TEST_ENV) go test -tags=e2e_testing -count=1 $(TEST_FLAGS) ./e2e
|
||||||
|
|
||||||
e2ev: TEST_FLAGS = -v
|
e2ev: TEST_FLAGS += -v
|
||||||
e2ev: e2e
|
e2ev: e2e
|
||||||
|
|
||||||
e2evv: TEST_ENV += TEST_LOGS=1
|
e2evv: TEST_ENV += TEST_LOGS=1
|
||||||
@ -96,7 +96,7 @@ release-netbsd: $(ALL_NETBSD:%=build/nebula-%.tar.gz)
|
|||||||
|
|
||||||
release-boringcrypto: build/nebula-linux-$(shell go env GOARCH)-boringcrypto.tar.gz
|
release-boringcrypto: build/nebula-linux-$(shell go env GOARCH)-boringcrypto.tar.gz
|
||||||
|
|
||||||
BUILD_ARGS = -trimpath
|
BUILD_ARGS += -trimpath
|
||||||
|
|
||||||
bin-windows: build/windows-amd64/nebula.exe build/windows-amd64/nebula-cert.exe
|
bin-windows: build/windows-amd64/nebula.exe build/windows-amd64/nebula-cert.exe
|
||||||
mv $? .
|
mv $? .
|
||||||
@ -116,6 +116,10 @@ bin-freebsd-arm64: build/freebsd-arm64/nebula build/freebsd-arm64/nebula-cert
|
|||||||
bin-boringcrypto: build/linux-$(shell go env GOARCH)-boringcrypto/nebula build/linux-$(shell go env GOARCH)-boringcrypto/nebula-cert
|
bin-boringcrypto: build/linux-$(shell go env GOARCH)-boringcrypto/nebula build/linux-$(shell go env GOARCH)-boringcrypto/nebula-cert
|
||||||
mv $? .
|
mv $? .
|
||||||
|
|
||||||
|
bin-pkcs11: BUILD_ARGS += -tags pkcs11
|
||||||
|
bin-pkcs11: CGO_ENABLED = 1
|
||||||
|
bin-pkcs11: bin
|
||||||
|
|
||||||
bin:
|
bin:
|
||||||
go build $(BUILD_ARGS) -ldflags "$(LDFLAGS)" -o ./nebula${NEBULA_CMD_SUFFIX} ${NEBULA_CMD_PATH}
|
go build $(BUILD_ARGS) -ldflags "$(LDFLAGS)" -o ./nebula${NEBULA_CMD_SUFFIX} ${NEBULA_CMD_PATH}
|
||||||
go build $(BUILD_ARGS) -ldflags "$(LDFLAGS)" -o ./nebula-cert${NEBULA_CMD_SUFFIX} ./cmd/nebula-cert
|
go build $(BUILD_ARGS) -ldflags "$(LDFLAGS)" -o ./nebula-cert${NEBULA_CMD_SUFFIX} ./cmd/nebula-cert
|
||||||
@ -168,6 +172,9 @@ test:
|
|||||||
test-boringcrypto:
|
test-boringcrypto:
|
||||||
GOEXPERIMENT=boringcrypto CGO_ENABLED=1 go test -v ./...
|
GOEXPERIMENT=boringcrypto CGO_ENABLED=1 go test -v ./...
|
||||||
|
|
||||||
|
test-pkcs11:
|
||||||
|
CGO_ENABLED=1 go test -v -tags pkcs11 ./...
|
||||||
|
|
||||||
test-cov-html:
|
test-cov-html:
|
||||||
go test -coverprofile=coverage.out
|
go test -coverprofile=coverage.out
|
||||||
go tool cover -html=coverage.out
|
go tool cover -html=coverage.out
|
||||||
|
|||||||
37
cert/cert.go
37
cert/cert.go
@ -20,6 +20,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/slackhq/nebula/pkclient"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
@ -41,8 +42,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NebulaCertificate struct {
|
type NebulaCertificate struct {
|
||||||
Details NebulaCertificateDetails
|
Details NebulaCertificateDetails
|
||||||
Signature []byte
|
Pkcs11Backed bool
|
||||||
|
Signature []byte
|
||||||
|
|
||||||
// the cached hex string of the calculated sha256sum
|
// the cached hex string of the calculated sha256sum
|
||||||
// for VerifyWithCache
|
// for VerifyWithCache
|
||||||
@ -555,6 +557,34 @@ func (nc *NebulaCertificate) Sign(curve Curve, key []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignPkcs11 signs a nebula cert with the provided private key
|
||||||
|
func (nc *NebulaCertificate) SignPkcs11(curve Curve, client *pkclient.PKClient) error {
|
||||||
|
if !nc.Pkcs11Backed {
|
||||||
|
return fmt.Errorf("certificate is not PKCS#11 backed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if curve != nc.Details.Curve {
|
||||||
|
return fmt.Errorf("curve in cert and private key supplied don't match")
|
||||||
|
}
|
||||||
|
|
||||||
|
if curve != Curve_P256 {
|
||||||
|
return fmt.Errorf("only P256 is supported by PKCS#11")
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := proto.Marshal(nc.getRawDetails())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := client.SignASN1(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nc.Signature = sig
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CheckSignature verifies the signature against the provided public key
|
// CheckSignature verifies the signature against the provided public key
|
||||||
func (nc *NebulaCertificate) CheckSignature(key []byte) bool {
|
func (nc *NebulaCertificate) CheckSignature(key []byte) bool {
|
||||||
b, err := proto.Marshal(nc.getRawDetails())
|
b, err := proto.Marshal(nc.getRawDetails())
|
||||||
@ -693,6 +723,9 @@ func (nc *NebulaCertificate) CheckRootConstrains(signer *NebulaCertificate) erro
|
|||||||
|
|
||||||
// VerifyPrivateKey checks that the public key in the Nebula certificate and a supplied private key match
|
// VerifyPrivateKey checks that the public key in the Nebula certificate and a supplied private key match
|
||||||
func (nc *NebulaCertificate) VerifyPrivateKey(curve Curve, key []byte) error {
|
func (nc *NebulaCertificate) VerifyPrivateKey(curve Curve, key []byte) error {
|
||||||
|
if nc.Pkcs11Backed {
|
||||||
|
return nil //todo!
|
||||||
|
}
|
||||||
if curve != nc.Details.Curve {
|
if curve != nc.Details.Curve {
|
||||||
return fmt.Errorf("curve in cert and private key supplied don't match")
|
return fmt.Errorf("curve in cert and private key supplied don't match")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/skip2/go-qrcode"
|
"github.com/skip2/go-qrcode"
|
||||||
"github.com/slackhq/nebula/cert"
|
"github.com/slackhq/nebula/cert"
|
||||||
|
"github.com/slackhq/nebula/pkclient"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,7 +35,8 @@ type caFlags struct {
|
|||||||
argonParallelism *uint
|
argonParallelism *uint
|
||||||
encryption *bool
|
encryption *bool
|
||||||
|
|
||||||
curve *string
|
curve *string
|
||||||
|
p11url *string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCaFlags() *caFlags {
|
func newCaFlags() *caFlags {
|
||||||
@ -52,6 +55,7 @@ func newCaFlags() *caFlags {
|
|||||||
cf.argonIterations = cf.set.Uint("argon-iterations", 1, "Optional: Argon2 iterations parameter used for encrypted private key passphrase")
|
cf.argonIterations = cf.set.Uint("argon-iterations", 1, "Optional: Argon2 iterations parameter used for encrypted private key passphrase")
|
||||||
cf.encryption = cf.set.Bool("encrypt", false, "Optional: prompt for passphrase and write out-key in an encrypted format")
|
cf.encryption = cf.set.Bool("encrypt", false, "Optional: prompt for passphrase and write out-key in an encrypted format")
|
||||||
cf.curve = cf.set.String("curve", "25519", "EdDSA/ECDSA Curve (25519, P256)")
|
cf.curve = cf.set.String("curve", "25519", "EdDSA/ECDSA Curve (25519, P256)")
|
||||||
|
cf.p11url = p11Flag(cf.set)
|
||||||
return &cf
|
return &cf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,17 +80,21 @@ func ca(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isP11 := len(*cf.p11url) > 0
|
||||||
|
|
||||||
if err := mustFlagString("name", cf.name); err != nil {
|
if err := mustFlagString("name", cf.name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := mustFlagString("out-key", cf.outKeyPath); err != nil {
|
if !isP11 {
|
||||||
return err
|
if err = mustFlagString("out-key", cf.outKeyPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := mustFlagString("out-crt", cf.outCertPath); err != nil {
|
if err := mustFlagString("out-crt", cf.outCertPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var kdfParams *cert.Argon2Parameters
|
var kdfParams *cert.Argon2Parameters
|
||||||
if *cf.encryption {
|
if !isP11 && *cf.encryption {
|
||||||
if kdfParams, err = parseArgonParameters(*cf.argonMemory, *cf.argonParallelism, *cf.argonIterations); err != nil {
|
if kdfParams, err = parseArgonParameters(*cf.argonMemory, *cf.argonParallelism, *cf.argonIterations); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -143,7 +151,7 @@ func ca(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
var passphrase []byte
|
var passphrase []byte
|
||||||
if *cf.encryption {
|
if !isP11 && *cf.encryption {
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
out.Write([]byte("Enter passphrase: "))
|
out.Write([]byte("Enter passphrase: "))
|
||||||
passphrase, err = pr.ReadPassword()
|
passphrase, err = pr.ReadPassword()
|
||||||
@ -166,29 +174,54 @@ func ca(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error
|
|||||||
|
|
||||||
var curve cert.Curve
|
var curve cert.Curve
|
||||||
var pub, rawPriv []byte
|
var pub, rawPriv []byte
|
||||||
switch *cf.curve {
|
var p11Client *pkclient.PKClient
|
||||||
case "25519", "X25519", "Curve25519", "CURVE25519":
|
|
||||||
curve = cert.Curve_CURVE25519
|
if isP11 {
|
||||||
pub, rawPriv, err = ed25519.GenerateKey(rand.Reader)
|
switch *cf.curve {
|
||||||
if err != nil {
|
case "P256":
|
||||||
return fmt.Errorf("error while generating ed25519 keys: %s", err)
|
curve = cert.Curve_P256
|
||||||
}
|
default:
|
||||||
case "P256":
|
return fmt.Errorf("invalid curve for PKCS#11: %s", *cf.curve)
|
||||||
var key *ecdsa.PrivateKey
|
|
||||||
curve = cert.Curve_P256
|
|
||||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error while generating ecdsa keys: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ecdh.PrivateKey lets us get at the encoded bytes, even though
|
p11Client, err = pkclient.FromUrl(*cf.p11url)
|
||||||
// we aren't using ECDH here.
|
|
||||||
eKey, err := key.ECDH()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while converting ecdsa key: %s", err)
|
return fmt.Errorf("error while creating PKCS#11 client: %w", err)
|
||||||
|
}
|
||||||
|
defer func(client *pkclient.PKClient) {
|
||||||
|
_ = client.Close()
|
||||||
|
}(p11Client)
|
||||||
|
pub, err = p11Client.GetPubKey()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while getting public key with PKCS#11: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch *cf.curve {
|
||||||
|
case "25519", "X25519", "Curve25519", "CURVE25519":
|
||||||
|
curve = cert.Curve_CURVE25519
|
||||||
|
pub, rawPriv, err = ed25519.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while generating ed25519 keys: %s", err)
|
||||||
|
}
|
||||||
|
case "P256":
|
||||||
|
var key *ecdsa.PrivateKey
|
||||||
|
curve = cert.Curve_P256
|
||||||
|
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while generating ecdsa keys: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ecdh.PrivateKey lets us get at the encoded bytes, even though
|
||||||
|
// we aren't using ECDH here.
|
||||||
|
eKey, err := key.ECDH()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while converting ecdsa key: %s", err)
|
||||||
|
}
|
||||||
|
rawPriv = eKey.Bytes()
|
||||||
|
pub = eKey.PublicKey().Bytes()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid curve: %s", *cf.curve)
|
||||||
}
|
}
|
||||||
rawPriv = eKey.Bytes()
|
|
||||||
pub = eKey.PublicKey().Bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nc := cert.NebulaCertificate{
|
nc := cert.NebulaCertificate{
|
||||||
@ -203,34 +236,48 @@ func ca(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error
|
|||||||
IsCA: true,
|
IsCA: true,
|
||||||
Curve: curve,
|
Curve: curve,
|
||||||
},
|
},
|
||||||
|
Pkcs11Backed: isP11,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(*cf.outKeyPath); err == nil {
|
if !isP11 {
|
||||||
return fmt.Errorf("refusing to overwrite existing CA key: %s", *cf.outKeyPath)
|
if _, err := os.Stat(*cf.outKeyPath); err == nil {
|
||||||
|
return fmt.Errorf("refusing to overwrite existing CA key: %s", *cf.outKeyPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(*cf.outCertPath); err == nil {
|
if _, err := os.Stat(*cf.outCertPath); err == nil {
|
||||||
return fmt.Errorf("refusing to overwrite existing CA cert: %s", *cf.outCertPath)
|
return fmt.Errorf("refusing to overwrite existing CA cert: %s", *cf.outCertPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nc.Sign(curve, rawPriv)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error while signing: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var b []byte
|
var b []byte
|
||||||
if *cf.encryption {
|
|
||||||
b, err = cert.EncryptAndMarshalSigningPrivateKey(curve, rawPriv, passphrase, kdfParams)
|
if isP11 {
|
||||||
|
err = nc.SignPkcs11(curve, p11Client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while encrypting out-key: %s", err)
|
return fmt.Errorf("error while signing with PKCS#11: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
b = cert.MarshalSigningPrivateKey(curve, rawPriv)
|
err = nc.Sign(curve, rawPriv)
|
||||||
}
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while signing: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
err = os.WriteFile(*cf.outKeyPath, b, 0600)
|
if *cf.encryption {
|
||||||
if err != nil {
|
b, err = cert.EncryptAndMarshalSigningPrivateKey(curve, rawPriv, passphrase, kdfParams)
|
||||||
return fmt.Errorf("error while writing out-key: %s", err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while encrypting out-key: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b = cert.MarshalSigningPrivateKey(curve, rawPriv)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.WriteFile(*cf.outKeyPath, b, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while writing out-key: %s", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(*cf.outCertPath); err == nil {
|
||||||
|
return fmt.Errorf("refusing to overwrite existing CA cert: %s", *cf.outCertPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err = nc.MarshalToPEM()
|
b, err = nc.MarshalToPEM()
|
||||||
|
|||||||
@ -52,6 +52,7 @@ func Test_caHelp(t *testing.T) {
|
|||||||
" \tOptional: path to write the private key to (default \"ca.key\")\n"+
|
" \tOptional: path to write the private key to (default \"ca.key\")\n"+
|
||||||
" -out-qr string\n"+
|
" -out-qr string\n"+
|
||||||
" \tOptional: output a qr code image (png) of the certificate\n"+
|
" \tOptional: output a qr code image (png) of the certificate\n"+
|
||||||
|
optionalPkcs11String(" -pkcs11 string\n \tOptional: PKCS#11 URI to an existing private key\n")+
|
||||||
" -subnets string\n"+
|
" -subnets string\n"+
|
||||||
" \tOptional: comma separated list of ipv4 address and network in CIDR notation. This will limit which ipv4 addresses and networks subordinate certs can use in subnets\n",
|
" \tOptional: comma separated list of ipv4 address and network in CIDR notation. This will limit which ipv4 addresses and networks subordinate certs can use in subnets\n",
|
||||||
ob.String(),
|
ob.String(),
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/slackhq/nebula/pkclient"
|
||||||
|
|
||||||
"github.com/slackhq/nebula/cert"
|
"github.com/slackhq/nebula/cert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,8 +15,8 @@ type keygenFlags struct {
|
|||||||
set *flag.FlagSet
|
set *flag.FlagSet
|
||||||
outKeyPath *string
|
outKeyPath *string
|
||||||
outPubPath *string
|
outPubPath *string
|
||||||
|
curve *string
|
||||||
curve *string
|
p11url *string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newKeygenFlags() *keygenFlags {
|
func newKeygenFlags() *keygenFlags {
|
||||||
@ -23,6 +25,7 @@ func newKeygenFlags() *keygenFlags {
|
|||||||
cf.outPubPath = cf.set.String("out-pub", "", "Required: path to write the public key to")
|
cf.outPubPath = cf.set.String("out-pub", "", "Required: path to write the public key to")
|
||||||
cf.outKeyPath = cf.set.String("out-key", "", "Required: path to write the private key to")
|
cf.outKeyPath = cf.set.String("out-key", "", "Required: path to write the private key to")
|
||||||
cf.curve = cf.set.String("curve", "25519", "ECDH Curve (25519, P256)")
|
cf.curve = cf.set.String("curve", "25519", "ECDH Curve (25519, P256)")
|
||||||
|
cf.p11url = p11Flag(cf.set)
|
||||||
return &cf
|
return &cf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,31 +36,57 @@ func keygen(args []string, out io.Writer, errOut io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mustFlagString("out-key", cf.outKeyPath); err != nil {
|
isP11 := len(*cf.p11url) > 0
|
||||||
return err
|
|
||||||
|
if !isP11 {
|
||||||
|
if err = mustFlagString("out-key", cf.outKeyPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := mustFlagString("out-pub", cf.outPubPath); err != nil {
|
if err = mustFlagString("out-pub", cf.outPubPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pub, rawPriv []byte
|
var pub, rawPriv []byte
|
||||||
var curve cert.Curve
|
var curve cert.Curve
|
||||||
switch *cf.curve {
|
if isP11 {
|
||||||
case "25519", "X25519", "Curve25519", "CURVE25519":
|
switch *cf.curve {
|
||||||
pub, rawPriv = x25519Keypair()
|
case "P256":
|
||||||
curve = cert.Curve_CURVE25519
|
curve = cert.Curve_P256
|
||||||
case "P256":
|
default:
|
||||||
pub, rawPriv = p256Keypair()
|
return fmt.Errorf("invalid curve for PKCS#11: %s", *cf.curve)
|
||||||
curve = cert.Curve_P256
|
}
|
||||||
default:
|
} else {
|
||||||
return fmt.Errorf("invalid curve: %s", *cf.curve)
|
switch *cf.curve {
|
||||||
|
case "25519", "X25519", "Curve25519", "CURVE25519":
|
||||||
|
pub, rawPriv = x25519Keypair()
|
||||||
|
curve = cert.Curve_CURVE25519
|
||||||
|
case "P256":
|
||||||
|
pub, rawPriv = p256Keypair()
|
||||||
|
curve = cert.Curve_P256
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid curve: %s", *cf.curve)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(*cf.outKeyPath, cert.MarshalPrivateKey(curve, rawPriv), 0600)
|
if isP11 {
|
||||||
if err != nil {
|
p11Client, err := pkclient.FromUrl(*cf.p11url)
|
||||||
return fmt.Errorf("error while writing out-key: %s", err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while creating PKCS#11 client: %w", err)
|
||||||
|
}
|
||||||
|
defer func(client *pkclient.PKClient) {
|
||||||
|
_ = client.Close()
|
||||||
|
}(p11Client)
|
||||||
|
pub, err = p11Client.GetPubKey()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while getting public key: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = os.WriteFile(*cf.outKeyPath, cert.MarshalPrivateKey(curve, rawPriv), 0600)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while writing out-key: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(*cf.outPubPath, cert.MarshalPublicKey(curve, pub), 0600)
|
err = os.WriteFile(*cf.outPubPath, cert.MarshalPublicKey(curve, pub), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while writing out-pub: %s", err)
|
return fmt.Errorf("error while writing out-pub: %s", err)
|
||||||
@ -72,7 +101,7 @@ func keygenSummary() string {
|
|||||||
|
|
||||||
func keygenHelp(out io.Writer) {
|
func keygenHelp(out io.Writer) {
|
||||||
cf := newKeygenFlags()
|
cf := newKeygenFlags()
|
||||||
out.Write([]byte("Usage of " + os.Args[0] + " " + keygenSummary() + "\n"))
|
_, _ = out.Write([]byte("Usage of " + os.Args[0] + " " + keygenSummary() + "\n"))
|
||||||
cf.set.SetOutput(out)
|
cf.set.SetOutput(out)
|
||||||
cf.set.PrintDefaults()
|
cf.set.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,8 @@ func Test_keygenHelp(t *testing.T) {
|
|||||||
" -out-key string\n"+
|
" -out-key string\n"+
|
||||||
" \tRequired: path to write the private key to\n"+
|
" \tRequired: path to write the private key to\n"+
|
||||||
" -out-pub string\n"+
|
" -out-pub string\n"+
|
||||||
" \tRequired: path to write the public key to\n",
|
" \tRequired: path to write the public key to\n"+
|
||||||
|
optionalPkcs11String(" -pkcs11 string\n \tOptional: PKCS#11 URI to an existing private key\n"),
|
||||||
ob.String(),
|
ob.String(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@ -77,8 +78,16 @@ func assertHelpError(t *testing.T, err error, msg string) {
|
|||||||
case *helpError:
|
case *helpError:
|
||||||
// good
|
// good
|
||||||
default:
|
default:
|
||||||
t.Fatal("err was not a helpError")
|
t.Fatal(fmt.Sprintf("err was not a helpError: %q, expected %q", err, msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.EqualError(t, err, msg)
|
assert.EqualError(t, err, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func optionalPkcs11String(msg string) string {
|
||||||
|
if p11Supported() {
|
||||||
|
return msg
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
15
cmd/nebula-cert/p11_cgo.go
Normal file
15
cmd/nebula-cert/p11_cgo.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//go:build cgo && pkcs11
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func p11Supported() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func p11Flag(set *flag.FlagSet) *string {
|
||||||
|
return set.String("pkcs11", "", "Optional: PKCS#11 URI to an existing private key")
|
||||||
|
}
|
||||||
16
cmd/nebula-cert/p11_stub.go
Normal file
16
cmd/nebula-cert/p11_stub.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//go:build !cgo || !pkcs11
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func p11Supported() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func p11Flag(set *flag.FlagSet) *string {
|
||||||
|
var ret = ""
|
||||||
|
return &ret
|
||||||
|
}
|
||||||
@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/skip2/go-qrcode"
|
"github.com/skip2/go-qrcode"
|
||||||
"github.com/slackhq/nebula/cert"
|
"github.com/slackhq/nebula/cert"
|
||||||
|
"github.com/slackhq/nebula/pkclient"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ type signFlags struct {
|
|||||||
outQRPath *string
|
outQRPath *string
|
||||||
groups *string
|
groups *string
|
||||||
subnets *string
|
subnets *string
|
||||||
|
p11url *string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSignFlags() *signFlags {
|
func newSignFlags() *signFlags {
|
||||||
@ -45,8 +47,8 @@ func newSignFlags() *signFlags {
|
|||||||
sf.outQRPath = sf.set.String("out-qr", "", "Optional: output a qr code image (png) of the certificate")
|
sf.outQRPath = sf.set.String("out-qr", "", "Optional: output a qr code image (png) of the certificate")
|
||||||
sf.groups = sf.set.String("groups", "", "Optional: comma separated list of groups")
|
sf.groups = sf.set.String("groups", "", "Optional: comma separated list of groups")
|
||||||
sf.subnets = sf.set.String("subnets", "", "Optional: comma separated list of ipv4 address and network in CIDR notation. Subnets this cert can serve for")
|
sf.subnets = sf.set.String("subnets", "", "Optional: comma separated list of ipv4 address and network in CIDR notation. Subnets this cert can serve for")
|
||||||
|
sf.p11url = p11Flag(sf.set)
|
||||||
return &sf
|
return &sf
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error {
|
func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error {
|
||||||
@ -56,8 +58,12 @@ func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mustFlagString("ca-key", sf.caKeyPath); err != nil {
|
isP11 := len(*sf.p11url) > 0
|
||||||
return err
|
|
||||||
|
if !isP11 {
|
||||||
|
if err := mustFlagString("ca-key", sf.caKeyPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := mustFlagString("ca-crt", sf.caCertPath); err != nil {
|
if err := mustFlagString("ca-crt", sf.caCertPath); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -68,47 +74,49 @@ func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader)
|
|||||||
if err := mustFlagString("ip", sf.ip); err != nil {
|
if err := mustFlagString("ip", sf.ip); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if *sf.inPubPath != "" && *sf.outKeyPath != "" {
|
if !isP11 && *sf.inPubPath != "" && *sf.outKeyPath != "" {
|
||||||
return newHelpErrorf("cannot set both -in-pub and -out-key")
|
return newHelpErrorf("cannot set both -in-pub and -out-key")
|
||||||
}
|
}
|
||||||
|
|
||||||
rawCAKey, err := os.ReadFile(*sf.caKeyPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error while reading ca-key: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var curve cert.Curve
|
var curve cert.Curve
|
||||||
var caKey []byte
|
var caKey []byte
|
||||||
|
if !isP11 {
|
||||||
// naively attempt to decode the private key as though it is not encrypted
|
var rawCAKey []byte
|
||||||
caKey, _, curve, err = cert.UnmarshalSigningPrivateKey(rawCAKey)
|
rawCAKey, err := os.ReadFile(*sf.caKeyPath)
|
||||||
if err == cert.ErrPrivateKeyEncrypted {
|
|
||||||
// ask for a passphrase until we get one
|
|
||||||
var passphrase []byte
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
out.Write([]byte("Enter passphrase: "))
|
|
||||||
passphrase, err = pr.ReadPassword()
|
|
||||||
|
|
||||||
if err == ErrNoTerminal {
|
|
||||||
return fmt.Errorf("ca-key is encrypted and must be decrypted interactively")
|
|
||||||
} else if err != nil {
|
|
||||||
return fmt.Errorf("error reading password: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(passphrase) > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(passphrase) == 0 {
|
|
||||||
return fmt.Errorf("cannot open encrypted ca-key without passphrase")
|
|
||||||
}
|
|
||||||
|
|
||||||
curve, caKey, _, err = cert.DecryptAndUnmarshalSigningPrivateKey(passphrase, rawCAKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while parsing encrypted ca-key: %s", err)
|
return fmt.Errorf("error while reading ca-key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// naively attempt to decode the private key as though it is not encrypted
|
||||||
|
caKey, _, curve, err = cert.UnmarshalSigningPrivateKey(rawCAKey)
|
||||||
|
if err == cert.ErrPrivateKeyEncrypted {
|
||||||
|
// ask for a passphrase until we get one
|
||||||
|
var passphrase []byte
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
out.Write([]byte("Enter passphrase: "))
|
||||||
|
passphrase, err = pr.ReadPassword()
|
||||||
|
|
||||||
|
if err == ErrNoTerminal {
|
||||||
|
return fmt.Errorf("ca-key is encrypted and must be decrypted interactively")
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("error reading password: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(passphrase) > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(passphrase) == 0 {
|
||||||
|
return fmt.Errorf("cannot open encrypted ca-key without passphrase")
|
||||||
|
}
|
||||||
|
|
||||||
|
curve, caKey, _, err = cert.DecryptAndUnmarshalSigningPrivateKey(passphrase, rawCAKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while parsing encrypted ca-key: %s", err)
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("error while parsing ca-key: %s", err)
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
|
||||||
return fmt.Errorf("error while parsing ca-key: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rawCACert, err := os.ReadFile(*sf.caCertPath)
|
rawCACert, err := os.ReadFile(*sf.caCertPath)
|
||||||
@ -121,8 +129,10 @@ func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader)
|
|||||||
return fmt.Errorf("error while parsing ca-crt: %s", err)
|
return fmt.Errorf("error while parsing ca-crt: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := caCert.VerifyPrivateKey(curve, caKey); err != nil {
|
if !isP11 {
|
||||||
return fmt.Errorf("refusing to sign, root certificate does not match private key")
|
if err := caCert.VerifyPrivateKey(curve, caKey); err != nil {
|
||||||
|
return fmt.Errorf("refusing to sign, root certificate does not match private key")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
issuer, err := caCert.Sha256Sum()
|
issuer, err := caCert.Sha256Sum()
|
||||||
@ -176,12 +186,25 @@ func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pub, rawPriv []byte
|
var pub, rawPriv []byte
|
||||||
|
var p11Client *pkclient.PKClient
|
||||||
|
|
||||||
|
if isP11 {
|
||||||
|
curve = cert.Curve_P256
|
||||||
|
p11Client, err = pkclient.FromUrl(*sf.p11url)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while creating PKCS#11 client: %w", err)
|
||||||
|
}
|
||||||
|
defer func(client *pkclient.PKClient) {
|
||||||
|
_ = client.Close()
|
||||||
|
}(p11Client)
|
||||||
|
}
|
||||||
|
|
||||||
if *sf.inPubPath != "" {
|
if *sf.inPubPath != "" {
|
||||||
|
var pubCurve cert.Curve
|
||||||
rawPub, err := os.ReadFile(*sf.inPubPath)
|
rawPub, err := os.ReadFile(*sf.inPubPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while reading in-pub: %s", err)
|
return fmt.Errorf("error while reading in-pub: %s", err)
|
||||||
}
|
}
|
||||||
var pubCurve cert.Curve
|
|
||||||
pub, _, pubCurve, err = cert.UnmarshalPublicKey(rawPub)
|
pub, _, pubCurve, err = cert.UnmarshalPublicKey(rawPub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while parsing in-pub: %s", err)
|
return fmt.Errorf("error while parsing in-pub: %s", err)
|
||||||
@ -189,6 +212,11 @@ func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader)
|
|||||||
if pubCurve != curve {
|
if pubCurve != curve {
|
||||||
return fmt.Errorf("curve of in-pub does not match ca")
|
return fmt.Errorf("curve of in-pub does not match ca")
|
||||||
}
|
}
|
||||||
|
} else if isP11 {
|
||||||
|
pub, err = p11Client.GetPubKey()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while getting public key with PKCS#11: %w", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pub, rawPriv = newKeypair(curve)
|
pub, rawPriv = newKeypair(curve)
|
||||||
}
|
}
|
||||||
@ -206,6 +234,19 @@ func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader)
|
|||||||
Issuer: issuer,
|
Issuer: issuer,
|
||||||
Curve: curve,
|
Curve: curve,
|
||||||
},
|
},
|
||||||
|
Pkcs11Backed: isP11,
|
||||||
|
}
|
||||||
|
|
||||||
|
if p11Client == nil {
|
||||||
|
err = nc.Sign(curve, caKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while signing: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = nc.SignPkcs11(curve, p11Client)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while signing with PKCS#11: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := nc.CheckRootConstrains(caCert); err != nil {
|
if err := nc.CheckRootConstrains(caCert); err != nil {
|
||||||
@ -224,12 +265,7 @@ func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader)
|
|||||||
return fmt.Errorf("refusing to overwrite existing cert: %s", *sf.outCertPath)
|
return fmt.Errorf("refusing to overwrite existing cert: %s", *sf.outCertPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nc.Sign(curve, caKey)
|
if !isP11 && *sf.inPubPath == "" {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error while signing: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *sf.inPubPath == "" {
|
|
||||||
if _, err := os.Stat(*sf.outKeyPath); err == nil {
|
if _, err := os.Stat(*sf.outKeyPath); err == nil {
|
||||||
return fmt.Errorf("refusing to overwrite existing key: %s", *sf.outKeyPath)
|
return fmt.Errorf("refusing to overwrite existing key: %s", *sf.outKeyPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,7 @@ func Test_signHelp(t *testing.T) {
|
|||||||
" \tOptional (if in-pub not set): path to write the private key to\n"+
|
" \tOptional (if in-pub not set): path to write the private key to\n"+
|
||||||
" -out-qr string\n"+
|
" -out-qr string\n"+
|
||||||
" \tOptional: output a qr code image (png) of the certificate\n"+
|
" \tOptional: output a qr code image (png) of the certificate\n"+
|
||||||
|
optionalPkcs11String(" -pkcs11 string\n \tOptional: PKCS#11 URI to an existing private key\n")+
|
||||||
" -subnets string\n"+
|
" -subnets string\n"+
|
||||||
" \tOptional: comma separated list of ipv4 address and network in CIDR notation. Subnets this cert can serve for\n",
|
" \tOptional: comma separated list of ipv4 address and network in CIDR notation. Subnets this cert can serve for\n",
|
||||||
ob.String(),
|
ob.String(),
|
||||||
|
|||||||
@ -32,7 +32,11 @@ func NewConnectionState(l *logrus.Logger, cipher string, certState *CertState, i
|
|||||||
case cert.Curve_CURVE25519:
|
case cert.Curve_CURVE25519:
|
||||||
dhFunc = noise.DH25519
|
dhFunc = noise.DH25519
|
||||||
case cert.Curve_P256:
|
case cert.Curve_P256:
|
||||||
dhFunc = noiseutil.DHP256
|
if certState.Certificate.Pkcs11Backed {
|
||||||
|
dhFunc = noiseutil.DHP256PKCS11
|
||||||
|
} else {
|
||||||
|
dhFunc = noiseutil.DHP256
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
l.Errorf("invalid curve: %s", certState.Certificate.Details.Curve)
|
l.Errorf("invalid curve: %s", certState.Certificate.Details.Curve)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
"github.com/slackhq/nebula/service"
|
"github.com/slackhq/nebula/service"
|
||||||
@ -54,16 +55,16 @@ pki:
|
|||||||
cert: /home/rice/Developer/nebula-config/app.crt
|
cert: /home/rice/Developer/nebula-config/app.crt
|
||||||
key: /home/rice/Developer/nebula-config/app.key
|
key: /home/rice/Developer/nebula-config/app.key
|
||||||
`
|
`
|
||||||
var config config.C
|
var cfg config.C
|
||||||
if err := config.LoadString(configStr); err != nil {
|
if err := cfg.LoadString(configStr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
service, err := service.New(&config)
|
svc, err := service.New(&cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ln, err := service.Listen("tcp", ":1234")
|
ln, err := svc.Listen("tcp", ":1234")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -73,16 +74,24 @@ pki:
|
|||||||
log.Printf("accept error: %s", err)
|
log.Printf("accept error: %s", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer func(conn net.Conn) {
|
||||||
|
_ = conn.Close()
|
||||||
|
}(conn)
|
||||||
|
|
||||||
log.Printf("got connection")
|
log.Printf("got connection")
|
||||||
|
|
||||||
conn.Write([]byte("hello world\n"))
|
_, err = conn.Write([]byte("hello world\n"))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("write error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(conn)
|
scanner := bufio.NewScanner(conn)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
message := scanner.Text()
|
message := scanner.Text()
|
||||||
fmt.Fprintf(conn, "echo: %q\n", message)
|
_, err = fmt.Fprintf(conn, "echo: %q\n", message)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("write error: %s", err)
|
||||||
|
}
|
||||||
log.Printf("got message %q", message)
|
log.Printf("got message %q", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,8 +101,8 @@ pki:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
service.Close()
|
_ = svc.Close()
|
||||||
if err := service.Wait(); err != nil {
|
if err := svc.Wait(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
16
go.mod
16
go.mod
@ -5,29 +5,32 @@ go 1.22.0
|
|||||||
toolchain go1.22.2
|
toolchain go1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0
|
dario.cat/mergo v1.0.1
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||||
github.com/armon/go-radix v1.0.0
|
github.com/armon/go-radix v1.0.0
|
||||||
github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432
|
github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432
|
||||||
github.com/flynn/noise v1.1.0
|
github.com/flynn/noise v1.1.0
|
||||||
|
github.com/gaissmai/bart v0.11.1
|
||||||
github.com/gogo/protobuf v1.3.2
|
github.com/gogo/protobuf v1.3.2
|
||||||
github.com/google/gopacket v1.1.19
|
github.com/google/gopacket v1.1.19
|
||||||
github.com/kardianos/service v1.2.2
|
github.com/kardianos/service v1.2.2
|
||||||
github.com/miekg/dns v1.1.61
|
github.com/miekg/dns v1.1.61
|
||||||
|
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b
|
||||||
github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f
|
github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f
|
||||||
github.com/prometheus/client_golang v1.19.1
|
github.com/prometheus/client_golang v1.19.1
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
|
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
||||||
|
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||||
golang.org/x/crypto v0.25.0
|
golang.org/x/crypto v0.26.0
|
||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
|
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
|
||||||
golang.org/x/net v0.27.0
|
golang.org/x/net v0.28.0
|
||||||
golang.org/x/sync v0.7.0
|
golang.org/x/sync v0.8.0
|
||||||
golang.org/x/sys v0.22.0
|
golang.org/x/sys v0.24.0
|
||||||
golang.org/x/term v0.22.0
|
golang.org/x/term v0.23.0
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b
|
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||||
@ -41,7 +44,6 @@ require (
|
|||||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/gaissmai/bart v0.11.1 // indirect
|
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.5.0 // indirect
|
||||||
|
|||||||
30
go.sum
30
go.sum
@ -1,6 +1,6 @@
|
|||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
@ -26,8 +26,6 @@ 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
||||||
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
||||||
github.com/gaissmai/bart v0.10.0 h1:yCZCYF8xzcRnqDe4jMk14NlJjL1WmMsE7ilBzvuHtiI=
|
|
||||||
github.com/gaissmai/bart v0.10.0/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
|
|
||||||
github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
|
github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
|
||||||
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
|
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
@ -85,6 +83,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
||||||
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
||||||
|
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b h1:J/AzCvg5z0Hn1rqZUJjpbzALUmkKX0Zwbc/i4fw7Sfk=
|
||||||
|
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
@ -133,6 +133,8 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1
|
|||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
|
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
|
||||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
|
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
|
||||||
|
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw=
|
||||||
|
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
@ -153,8 +155,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
@ -173,8 +175,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -182,8 +184,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -201,11 +203,11 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.24.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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
|||||||
@ -35,7 +35,7 @@ var (
|
|||||||
|
|
||||||
type HandshakeConfig struct {
|
type HandshakeConfig struct {
|
||||||
tryInterval time.Duration
|
tryInterval time.Duration
|
||||||
retries int
|
retries int64
|
||||||
triggerBuffer int
|
triggerBuffer int
|
||||||
useRelays bool
|
useRelays bool
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ type HandshakeHostInfo struct {
|
|||||||
|
|
||||||
startTime time.Time // Time that we first started trying with this handshake
|
startTime time.Time // Time that we first started trying with this handshake
|
||||||
ready bool // Is the handshake ready
|
ready bool // Is the handshake ready
|
||||||
counter int // How many attempts have we made so far
|
counter int64 // How many attempts have we made so far
|
||||||
lastRemotes []netip.AddrPort // Remotes that we sent to during the previous attempt
|
lastRemotes []netip.AddrPort // Remotes that we sent to during the previous attempt
|
||||||
packetStore []*cachedPacket // A set of packets to be transmitted once the handshake completes
|
packetStore []*cachedPacket // A set of packets to be transmitted once the handshake completes
|
||||||
|
|
||||||
@ -665,6 +665,6 @@ func generateIndex(l *logrus.Logger) (uint32, error) {
|
|||||||
return index, nil
|
return index, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hsTimeout(tries int, interval time.Duration) time.Duration {
|
func hsTimeout(tries int64, interval time.Duration) time.Duration {
|
||||||
return time.Duration(tries / 2 * ((2 * int(interval)) + (tries-1)*int(interval)))
|
return time.Duration(tries / 2 * ((2 * int64(interval)) + (tries-1)*int64(interval)))
|
||||||
}
|
}
|
||||||
|
|||||||
2
main.go
2
main.go
@ -215,7 +215,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg
|
|||||||
|
|
||||||
handshakeConfig := HandshakeConfig{
|
handshakeConfig := HandshakeConfig{
|
||||||
tryInterval: c.GetDuration("handshakes.try_interval", DefaultHandshakeTryInterval),
|
tryInterval: c.GetDuration("handshakes.try_interval", DefaultHandshakeTryInterval),
|
||||||
retries: c.GetInt("handshakes.retries", DefaultHandshakeRetries),
|
retries: int64(c.GetInt("handshakes.retries", DefaultHandshakeRetries)),
|
||||||
triggerBuffer: c.GetInt("handshakes.trigger_buffer", DefaultHandshakeTriggerBuffer),
|
triggerBuffer: c.GetInt("handshakes.trigger_buffer", DefaultHandshakeTriggerBuffer),
|
||||||
useRelays: useRelays,
|
useRelays: useRelays,
|
||||||
|
|
||||||
|
|||||||
50
noiseutil/pkcs11.go
Normal file
50
noiseutil/pkcs11.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package noiseutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdh"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/slackhq/nebula/pkclient"
|
||||||
|
|
||||||
|
"github.com/flynn/noise"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DHP256PKCS11 is the NIST P-256 ECDH function
|
||||||
|
var DHP256PKCS11 noise.DHFunc = newNISTP11Curve("P256", ecdh.P256(), 32)
|
||||||
|
|
||||||
|
type nistP11Curve struct {
|
||||||
|
nistCurve
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNISTP11Curve(name string, curve ecdh.Curve, byteLen int) nistP11Curve {
|
||||||
|
return nistP11Curve{
|
||||||
|
newNISTCurve(name, curve, byteLen),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c nistP11Curve) DH(privkey, pubkey []byte) ([]byte, error) {
|
||||||
|
//for this function "privkey" is actually a pkcs11 URI
|
||||||
|
pkStr := string(privkey)
|
||||||
|
|
||||||
|
//to set up a handshake, we need to also do non-pkcs11-DH. Handle that here.
|
||||||
|
if !strings.HasPrefix(pkStr, "pkcs11:") {
|
||||||
|
return DHP256.DH(privkey, pubkey)
|
||||||
|
}
|
||||||
|
ecdhPubKey, err := c.curve.NewPublicKey(pubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to unmarshal pubkey: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//this is not the most performant way to do this (a long-lived client would be better)
|
||||||
|
//but, it works, and helps avoid problems with stale sessions and HSMs used by multiple users.
|
||||||
|
client, err := pkclient.FromUrl(pkStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func(client *pkclient.PKClient) {
|
||||||
|
_ = client.Close()
|
||||||
|
}(client)
|
||||||
|
|
||||||
|
return client.DeriveNoise(ecdhPubKey.Bytes())
|
||||||
|
}
|
||||||
87
pkclient/pkclient.go
Normal file
87
pkclient/pkclient.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package pkclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/stefanberger/go-pkcs11uri"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
io.Closer
|
||||||
|
GetPubKey() ([]byte, error)
|
||||||
|
DeriveNoise(peerPubKey []byte) ([]byte, error)
|
||||||
|
Test() error
|
||||||
|
}
|
||||||
|
|
||||||
|
const NoiseKeySize = 32
|
||||||
|
|
||||||
|
func FromUrl(pkurl string) (*PKClient, error) {
|
||||||
|
uri := pkcs11uri.New()
|
||||||
|
uri.SetAllowAnyModule(true) //todo
|
||||||
|
err := uri.Parse(pkurl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
module, err := uri.GetModule()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
slotid := 0
|
||||||
|
slot, ok := uri.GetPathAttribute("slot-id", false)
|
||||||
|
if !ok {
|
||||||
|
slotid = 0
|
||||||
|
} else {
|
||||||
|
slotid, err = strconv.Atoi(slot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pin, _ := uri.GetPIN()
|
||||||
|
id, _ := uri.GetPathAttribute("id", false)
|
||||||
|
label, _ := uri.GetPathAttribute("object", false)
|
||||||
|
|
||||||
|
return New(module, uint(slotid), pin, id, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ecKeyToArray(key *ecdsa.PublicKey) []byte {
|
||||||
|
x := make([]byte, 32)
|
||||||
|
y := make([]byte, 32)
|
||||||
|
key.X.FillBytes(x)
|
||||||
|
key.Y.FillBytes(y)
|
||||||
|
return append([]byte{0x04}, append(x, y...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPubkeyFromPublicKeyInfoAttr(d []byte) ([]byte, error) {
|
||||||
|
e, err := x509.ParsePKIXPublicKey(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch t := e.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
return ecKeyToArray(e.(*ecdsa.PublicKey)), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown public key type: %T", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PKClient) Test() error {
|
||||||
|
pub, err := c.GetPubKey()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get public key: %w", err)
|
||||||
|
}
|
||||||
|
out, err := c.DeriveNoise(pub) //do an ECDH with ourselves as a quick test
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(out) != NoiseKeySize {
|
||||||
|
return fmt.Errorf("got a key of %d bytes, expected %d", len(out), NoiseKeySize)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
229
pkclient/pkclient_cgo.go
Normal file
229
pkclient/pkclient_cgo.go
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
//go:build cgo && pkcs11
|
||||||
|
|
||||||
|
package pkclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/miekg/pkcs11"
|
||||||
|
"github.com/miekg/pkcs11/p11"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PKClient struct {
|
||||||
|
module p11.Module
|
||||||
|
session p11.Session
|
||||||
|
id []byte
|
||||||
|
label []byte
|
||||||
|
privKeyObj p11.Object
|
||||||
|
pubKeyObj p11.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
type ecdsaSignature struct {
|
||||||
|
R, S *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// New tries to open a session with the HSM, select the slot and login to it
|
||||||
|
func New(hsmPath string, slotId uint, pin string, id string, label string) (*PKClient, error) {
|
||||||
|
module, err := p11.OpenModule(hsmPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load module library: %s", hsmPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
slots, err := module.Slots()
|
||||||
|
if err != nil {
|
||||||
|
module.Destroy()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to open a session on the slot
|
||||||
|
slotIdx := 0
|
||||||
|
for i, slot := range slots {
|
||||||
|
if slot.ID() == slotId {
|
||||||
|
slotIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &PKClient{
|
||||||
|
module: module,
|
||||||
|
id: []byte(id),
|
||||||
|
label: []byte(label),
|
||||||
|
}
|
||||||
|
|
||||||
|
client.session, err = slots[slotIdx].OpenWriteSession()
|
||||||
|
if err != nil {
|
||||||
|
module.Destroy()
|
||||||
|
return nil, fmt.Errorf("failed to open session on slot %d", slotId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pin) != 0 {
|
||||||
|
err = client.session.Login(pin)
|
||||||
|
if err != nil {
|
||||||
|
// ignore "already logged in"
|
||||||
|
if !errors.Is(err, pkcs11.Error(256)) {
|
||||||
|
_ = client.session.Close()
|
||||||
|
return nil, fmt.Errorf("unable to login. error: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the hsm has a private key for deriving
|
||||||
|
client.privKeyObj, err = client.findDeriveKey(client.id, client.label, true)
|
||||||
|
if err != nil {
|
||||||
|
_ = client.Close() //log out, close session, destroy module
|
||||||
|
return nil, fmt.Errorf("failed to find private key for deriving: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cleans up properly and logs out
|
||||||
|
func (c *PKClient) Close() error {
|
||||||
|
var err error = nil
|
||||||
|
if c.session != nil {
|
||||||
|
_ = c.session.Logout() //if logout fails, we still want to close
|
||||||
|
err = c.session.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
c.module.Destroy()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find a suitable key on the hsm for key derivation
|
||||||
|
// parameter GET_PUB_KEY sets the search pattern for a public or private key
|
||||||
|
func (c *PKClient) findDeriveKey(id []byte, label []byte, private bool) (key p11.Object, err error) {
|
||||||
|
keyClass := pkcs11.CKO_PRIVATE_KEY
|
||||||
|
if !private {
|
||||||
|
keyClass = pkcs11.CKO_PUBLIC_KEY
|
||||||
|
}
|
||||||
|
keyAttrs := []*pkcs11.Attribute{
|
||||||
|
//todo, not all HSMs seem to report this, even if its true: pkcs11.NewAttribute(pkcs11.CKA_DERIVE, true),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_CLASS, keyClass),
|
||||||
|
}
|
||||||
|
|
||||||
|
if id != nil && len(id) != 0 {
|
||||||
|
keyAttrs = append(keyAttrs, pkcs11.NewAttribute(pkcs11.CKA_ID, id))
|
||||||
|
}
|
||||||
|
if label != nil && len(label) != 0 {
|
||||||
|
keyAttrs = append(keyAttrs, pkcs11.NewAttribute(pkcs11.CKA_LABEL, label))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.session.FindObject(keyAttrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PKClient) listDeriveKeys(id []byte, label []byte, private bool) {
|
||||||
|
keyClass := pkcs11.CKO_PRIVATE_KEY
|
||||||
|
if !private {
|
||||||
|
keyClass = pkcs11.CKO_PUBLIC_KEY
|
||||||
|
}
|
||||||
|
keyAttrs := []*pkcs11.Attribute{
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_CLASS, keyClass),
|
||||||
|
}
|
||||||
|
|
||||||
|
if id != nil && len(id) != 0 {
|
||||||
|
keyAttrs = append(keyAttrs, pkcs11.NewAttribute(pkcs11.CKA_ID, id))
|
||||||
|
}
|
||||||
|
if label != nil && len(label) != 0 {
|
||||||
|
keyAttrs = append(keyAttrs, pkcs11.NewAttribute(pkcs11.CKA_LABEL, label))
|
||||||
|
}
|
||||||
|
|
||||||
|
objects, err := c.session.FindObjects(keyAttrs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, obj := range objects {
|
||||||
|
l, err := obj.Label()
|
||||||
|
log.Printf("%s, %v", l, err)
|
||||||
|
a, err := obj.Attribute(pkcs11.CKA_DERIVE)
|
||||||
|
log.Printf("DERIVE: %s %v, %v", l, a, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignASN1 signs some data. Returns the ASN.1 encoded signature.
|
||||||
|
func (c *PKClient) SignASN1(data []byte) ([]byte, error) {
|
||||||
|
mech := pkcs11.NewMechanism(pkcs11.CKM_ECDSA_SHA256, nil)
|
||||||
|
sk := p11.PrivateKey(c.privKeyObj)
|
||||||
|
rawSig, err := sk.Sign(*mech, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PKCS #11 Mechanisms v2.30:
|
||||||
|
// "The signature octets correspond to the concatenation of the ECDSA values r and s,
|
||||||
|
// both represented as an octet string of equal length of at most nLen with the most
|
||||||
|
// significant byte first. If r and s have different octet length, the shorter of both
|
||||||
|
// must be padded with leading zero octets such that both have the same octet length.
|
||||||
|
// Loosely spoken, the first half of the signature is r and the second half is s."
|
||||||
|
r := new(big.Int).SetBytes(rawSig[:len(rawSig)/2])
|
||||||
|
s := new(big.Int).SetBytes(rawSig[len(rawSig)/2:])
|
||||||
|
return asn1.Marshal(ecdsaSignature{r, s})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeriveNoise derives a shared secret using the input public key against the private key that was found during setup.
|
||||||
|
// Returns a fixed 32 byte array.
|
||||||
|
func (c *PKClient) DeriveNoise(peerPubKey []byte) ([]byte, error) {
|
||||||
|
// Before we call derive, we need to have an array of attributes which specify the type of
|
||||||
|
// key to be returned, in our case, it's the shared secret key, produced via deriving
|
||||||
|
// This template pulled from OpenSC pkclient-tool.c line 4038
|
||||||
|
attrTemplate := []*pkcs11.Attribute{
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, false),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_SECRET_KEY),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_GENERIC_SECRET),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, false),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, true),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_ENCRYPT, true),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_DECRYPT, true),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_WRAP, true),
|
||||||
|
pkcs11.NewAttribute(pkcs11.CKA_UNWRAP, true),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the parameters which include the peer's public key
|
||||||
|
ecdhParams := pkcs11.NewECDH1DeriveParams(pkcs11.CKD_NULL, nil, peerPubKey)
|
||||||
|
mech := pkcs11.NewMechanism(pkcs11.CKM_ECDH1_DERIVE, ecdhParams)
|
||||||
|
sk := p11.PrivateKey(c.privKeyObj)
|
||||||
|
|
||||||
|
tmpKey, err := sk.Derive(*mech, attrTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tmpKey == nil || len(tmpKey) == 0 {
|
||||||
|
return nil, fmt.Errorf("got an empty secret key")
|
||||||
|
}
|
||||||
|
secret := make([]byte, NoiseKeySize)
|
||||||
|
copy(secret[:], tmpKey[:NoiseKeySize])
|
||||||
|
return secret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PKClient) GetPubKey() ([]byte, error) {
|
||||||
|
d, err := c.privKeyObj.Attribute(pkcs11.CKA_PUBLIC_KEY_INFO)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if d != nil && len(d) > 0 {
|
||||||
|
return formatPubkeyFromPublicKeyInfoAttr(d)
|
||||||
|
}
|
||||||
|
c.pubKeyObj, err = c.findDeriveKey(c.id, c.label, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("pkcs11 module gave us a nil CKA_PUBLIC_KEY_INFO, and looking up the public key also failed: %w", err)
|
||||||
|
}
|
||||||
|
d, err = c.pubKeyObj.Attribute(pkcs11.CKA_EC_POINT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("pkcs11 module gave us a nil CKA_PUBLIC_KEY_INFO, and reading CKA_EC_POINT also failed: %w", err)
|
||||||
|
}
|
||||||
|
if d == nil || len(d) < 1 {
|
||||||
|
return nil, fmt.Errorf("pkcs11 module gave us a nil or empty CKA_EC_POINT")
|
||||||
|
}
|
||||||
|
switch len(d) {
|
||||||
|
case 65: //length of 0x04 + len(X) + len(Y)
|
||||||
|
return d, nil
|
||||||
|
case 67: //as above, DER-encoded IIRC?
|
||||||
|
return d[2:], nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown public key length: %d", len(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
30
pkclient/pkclient_stub.go
Normal file
30
pkclient/pkclient_stub.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//go:build !cgo || !pkcs11
|
||||||
|
|
||||||
|
package pkclient
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
type PKClient struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var notImplemented = errors.New("not implemented")
|
||||||
|
|
||||||
|
func New(hsmPath string, slotId uint, pin string, id string, label string) (*PKClient, error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PKClient) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PKClient) SignASN1(data []byte) ([]byte, error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PKClient) DeriveNoise(_ []byte) ([]byte, error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PKClient) GetPubKey() ([]byte, error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
44
pki.go
44
pki.go
@ -141,8 +141,33 @@ func newCertState(certificate *cert.NebulaCertificate, privateKey []byte) (*Cert
|
|||||||
return cs, nil
|
return cs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCertStateFromConfig(c *config.C) (*CertState, error) {
|
func loadPrivateKey(privPathOrPEM string) (rawKey []byte, curve cert.Curve, isPkcs11 bool, err error) {
|
||||||
var pemPrivateKey []byte
|
var pemPrivateKey []byte
|
||||||
|
if strings.Contains(privPathOrPEM, "-----BEGIN") {
|
||||||
|
pemPrivateKey = []byte(privPathOrPEM)
|
||||||
|
privPathOrPEM = "<inline>"
|
||||||
|
rawKey, _, curve, err = cert.UnmarshalPrivateKey(pemPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, curve, false, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err)
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(privPathOrPEM, "pkcs11:") {
|
||||||
|
rawKey = []byte(privPathOrPEM)
|
||||||
|
return rawKey, cert.Curve_P256, true, nil
|
||||||
|
} else {
|
||||||
|
pemPrivateKey, err = os.ReadFile(privPathOrPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, curve, false, fmt.Errorf("unable to read pki.key file %s: %s", privPathOrPEM, err)
|
||||||
|
}
|
||||||
|
rawKey, _, curve, err = cert.UnmarshalPrivateKey(pemPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, curve, false, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCertStateFromConfig(c *config.C) (*CertState, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
privPathOrPEM := c.GetString("pki.key", "")
|
privPathOrPEM := c.GetString("pki.key", "")
|
||||||
@ -150,20 +175,9 @@ func newCertStateFromConfig(c *config.C) (*CertState, error) {
|
|||||||
return nil, errors.New("no pki.key path or PEM data provided")
|
return nil, errors.New("no pki.key path or PEM data provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(privPathOrPEM, "-----BEGIN") {
|
rawKey, curve, isPkcs11, err := loadPrivateKey(privPathOrPEM)
|
||||||
pemPrivateKey = []byte(privPathOrPEM)
|
|
||||||
privPathOrPEM = "<inline>"
|
|
||||||
|
|
||||||
} else {
|
|
||||||
pemPrivateKey, err = os.ReadFile(privPathOrPEM)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to read pki.key file %s: %s", privPathOrPEM, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rawKey, _, curve, err := cert.UnmarshalPrivateKey(pemPrivateKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var rawCert []byte
|
var rawCert []byte
|
||||||
@ -188,7 +202,7 @@ func newCertStateFromConfig(c *config.C) (*CertState, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error while unmarshaling pki.cert %s: %s", pubPathOrPEM, err)
|
return nil, fmt.Errorf("error while unmarshaling pki.cert %s: %s", pubPathOrPEM, err)
|
||||||
}
|
}
|
||||||
|
nebulaCert.Pkcs11Backed = isPkcs11
|
||||||
if nebulaCert.Expired(time.Now()) {
|
if nebulaCert.Expired(time.Now()) {
|
||||||
return nil, fmt.Errorf("nebula certificate for this host is expired")
|
return nil, fmt.Errorf("nebula certificate for this host is expired")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -153,24 +154,48 @@ func New(config *config.C) (*Service, error) {
|
|||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext dials the provided address. Currently only TCP is supported.
|
func getProtocolNumber(addr netip.Addr) tcpip.NetworkProtocolNumber {
|
||||||
|
if addr.Is6() {
|
||||||
|
return ipv6.ProtocolNumber
|
||||||
|
}
|
||||||
|
return ipv4.ProtocolNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialContext dials the provided address.
|
||||||
func (s *Service) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
func (s *Service) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
if network != "tcp" && network != "tcp4" {
|
switch network {
|
||||||
return nil, errors.New("only tcp is supported")
|
case "udp", "udp4", "udp6":
|
||||||
|
addr, err := net.ResolveUDPAddr(network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fullAddr := tcpip.FullAddress{
|
||||||
|
NIC: nicID,
|
||||||
|
Addr: tcpip.AddrFromSlice(addr.IP),
|
||||||
|
Port: uint16(addr.Port),
|
||||||
|
}
|
||||||
|
num := getProtocolNumber(addr.AddrPort().Addr())
|
||||||
|
return gonet.DialUDP(s.ipstack, nil, &fullAddr, num)
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
addr, err := net.ResolveTCPAddr(network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fullAddr := tcpip.FullAddress{
|
||||||
|
NIC: nicID,
|
||||||
|
Addr: tcpip.AddrFromSlice(addr.IP),
|
||||||
|
Port: uint16(addr.Port),
|
||||||
|
}
|
||||||
|
num := getProtocolNumber(addr.AddrPort().Addr())
|
||||||
|
return gonet.DialContextTCP(ctx, s.ipstack, fullAddr, num)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown network type: %s", network)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addr, err := net.ResolveTCPAddr(network, address)
|
// Dial dials the provided address
|
||||||
if err != nil {
|
func (s *Service) Dial(network, address string) (net.Conn, error) {
|
||||||
return nil, err
|
return s.DialContext(context.Background(), network, address)
|
||||||
}
|
|
||||||
|
|
||||||
fullAddr := tcpip.FullAddress{
|
|
||||||
NIC: nicID,
|
|
||||||
Addr: tcpip.AddrFromSlice(addr.IP),
|
|
||||||
Port: uint16(addr.Port),
|
|
||||||
}
|
|
||||||
|
|
||||||
return gonet.DialContextTCP(ctx, s.ipstack, fullAddr, ipv4.ProtocolNumber)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen listens on the provided address. Currently only TCP with wildcard
|
// Listen listens on the provided address. Currently only TCP with wildcard
|
||||||
|
|||||||
@ -218,9 +218,7 @@ func (u *StdConn) writeTo6(b []byte, ip netip.AddrPort) error {
|
|||||||
var rsa unix.RawSockaddrInet6
|
var rsa unix.RawSockaddrInet6
|
||||||
rsa.Family = unix.AF_INET6
|
rsa.Family = unix.AF_INET6
|
||||||
rsa.Addr = ip.Addr().As16()
|
rsa.Addr = ip.Addr().As16()
|
||||||
port := ip.Port()
|
binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ip.Port())
|
||||||
// Little Endian -> Network Endian
|
|
||||||
rsa.Port = (port >> 8) | ((port & 0xff) << 8)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, _, err := unix.Syscall6(
|
_, _, err := unix.Syscall6(
|
||||||
@ -251,9 +249,7 @@ func (u *StdConn) writeTo4(b []byte, ip netip.AddrPort) error {
|
|||||||
var rsa unix.RawSockaddrInet4
|
var rsa unix.RawSockaddrInet4
|
||||||
rsa.Family = unix.AF_INET
|
rsa.Family = unix.AF_INET
|
||||||
rsa.Addr = ip.Addr().As4()
|
rsa.Addr = ip.Addr().As4()
|
||||||
port := ip.Port()
|
binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ip.Port())
|
||||||
// Little Endian -> Network Endian
|
|
||||||
rsa.Port = (port >> 8) | ((port & 0xff) << 8)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, _, err := unix.Syscall6(
|
_, _, err := unix.Syscall6(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user