Update dependencies
This commit is contained in:
parent
bf5616c65b
commit
d6d374b28d
13962 changed files with 48226 additions and 3618880 deletions
58
pkg/net/dns/dns.go
Normal file
58
pkg/net/dns/dns.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
var defResolvConf = "/etc/resolv.conf"
|
||||
|
||||
// GetSystemNameServers returns the list of nameservers located in the file /etc/resolv.conf
|
||||
func GetSystemNameServers() ([]net.IP, error) {
|
||||
var nameservers []net.IP
|
||||
file, err := ioutil.ReadFile(defResolvConf)
|
||||
if err != nil {
|
||||
return nameservers, err
|
||||
}
|
||||
|
||||
// Lines of the form "nameserver 1.2.3.4" accumulate.
|
||||
lines := strings.Split(string(file), "\n")
|
||||
for l := range lines {
|
||||
trimmed := strings.TrimSpace(lines[l])
|
||||
if len(trimmed) == 0 || trimmed[0] == '#' || trimmed[0] == ';' {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(trimmed)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
if fields[0] == "nameserver" {
|
||||
ip := net.ParseIP(fields[1])
|
||||
if ip != nil {
|
||||
nameservers = append(nameservers, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(3).Infof("nameservers IP address/es to use: %v", nameservers)
|
||||
return nameservers, nil
|
||||
}
|
||||
63
pkg/net/dns/dns_test.go
Normal file
63
pkg/net/dns/dns_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetDNSServers(t *testing.T) {
|
||||
s, err := GetSystemNameServers()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error reading /etc/resolv.conf file: %v", err)
|
||||
}
|
||||
if len(s) < 1 {
|
||||
t.Error("expected at least 1 nameserver in /etc/resolv.conf")
|
||||
}
|
||||
|
||||
file, err := ioutil.TempFile("", "fw")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
ioutil.WriteFile(file.Name(), []byte(`
|
||||
# comment
|
||||
; comment
|
||||
nameserver 2001:4860:4860::8844
|
||||
nameserver 2001:4860:4860::8888
|
||||
nameserver 8.8.8.8
|
||||
`), 0644)
|
||||
|
||||
defResolvConf = file.Name()
|
||||
s, err = GetSystemNameServers()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error reading /etc/resolv.conf file: %v", err)
|
||||
}
|
||||
if len(s) < 3 {
|
||||
t.Errorf("expected at 3 nameservers but %v returned", len(s))
|
||||
}
|
||||
|
||||
eip := net.ParseIP("2001:4860:4860::8844")
|
||||
if !s[0].Equal(eip) {
|
||||
t.Errorf("expected %v as nameservers but %v returned", eip, s[0])
|
||||
}
|
||||
}
|
||||
53
pkg/net/ipnet.go
Normal file
53
pkg/net/ipnet.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IPNet maps string to net.IPNet.
|
||||
type IPNet map[string]*net.IPNet
|
||||
|
||||
// IP maps string to net.IP.
|
||||
type IP map[string]net.IP
|
||||
|
||||
// ParseIPNets parses string slice to IPNet.
|
||||
func ParseIPNets(specs ...string) (IPNet, IP, error) {
|
||||
ipnetset := make(IPNet)
|
||||
ipset := make(IP)
|
||||
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
_, ipnet, err := net.ParseCIDR(spec)
|
||||
if err != nil {
|
||||
ip := net.ParseIP(spec)
|
||||
if ip == nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
i := ip.String()
|
||||
ipset[i] = ip
|
||||
continue
|
||||
}
|
||||
|
||||
k := ipnet.String()
|
||||
ipnetset[k] = ipnet
|
||||
}
|
||||
|
||||
return ipnetset, ipset, nil
|
||||
}
|
||||
34
pkg/net/ipnet_test.go
Normal file
34
pkg/net/ipnet_test.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewIPSet(t *testing.T) {
|
||||
ipsets, ips, err := ParseIPNets("1.0.0.0", "2.0.0.0/8", "3.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
if len(ipsets) != 2 {
|
||||
t.Errorf("Expected len=2: %d", len(ipsets))
|
||||
}
|
||||
if len(ips) != 1 {
|
||||
t.Errorf("Expected len=1: %d", len(ips))
|
||||
}
|
||||
}
|
||||
24
pkg/net/net.go
Normal file
24
pkg/net/net.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package net
|
||||
|
||||
import _net "net"
|
||||
|
||||
// IsIPV6 checks if the input contains a valid IPV6 address
|
||||
func IsIPV6(ip _net.IP) bool {
|
||||
return ip.To4() == nil
|
||||
}
|
||||
43
pkg/net/net_test.go
Normal file
43
pkg/net/net_test.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsIPV6(t *testing.T) {
|
||||
tests := []struct {
|
||||
in net.IP
|
||||
isIPV6 bool
|
||||
}{
|
||||
{net.ParseIP("2001:4860:4860::8844"), true},
|
||||
{net.ParseIP("2001:4860:4860::8888"), true},
|
||||
{net.ParseIP("0:0:0:0:0:ffff:c868:8165"), true},
|
||||
{net.ParseIP("2001:db8:85a3::8a2e:370:7334"), true},
|
||||
{net.ParseIP("::1"), true},
|
||||
{net.ParseIP("8.8.8.8"), false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
isIPV6 := IsIPV6(test.in)
|
||||
if isIPV6 && !test.isIPV6 {
|
||||
t.Fatalf("%v expected %v but returned %v", test.in, test.isIPV6, isIPV6)
|
||||
}
|
||||
}
|
||||
}
|
||||
420
pkg/net/ssl/ssl.go
Normal file
420
pkg/net/ssl/ssl.go
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ssl
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/zakjan/cert-chain-resolver/certUtil"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/file"
|
||||
"k8s.io/ingress-nginx/pkg/ingress"
|
||||
)
|
||||
|
||||
var (
|
||||
oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17}
|
||||
)
|
||||
|
||||
// AddOrUpdateCertAndKey creates a .pem file wth the cert and the key with the specified name
|
||||
func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, error) {
|
||||
pemName := fmt.Sprintf("%v.pem", name)
|
||||
pemFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, pemName)
|
||||
fullChainPemFileName := fmt.Sprintf("%v/%v-full-chain.pem", ingress.DefaultSSLDirectory, name)
|
||||
|
||||
tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create temp pem file %v: %v", pemFileName, err)
|
||||
}
|
||||
glog.V(3).Infof("Creating temp file %v for Keypair: %v", tempPemFile.Name(), pemName)
|
||||
|
||||
_, err = tempPemFile.Write(cert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not write to pem file %v: %v", tempPemFile.Name(), err)
|
||||
}
|
||||
_, err = tempPemFile.Write([]byte("\n"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not write to pem file %v: %v", tempPemFile.Name(), err)
|
||||
}
|
||||
_, err = tempPemFile.Write(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not write to pem file %v: %v", tempPemFile.Name(), err)
|
||||
}
|
||||
|
||||
err = tempPemFile.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not close temp pem file %v: %v", tempPemFile.Name(), err)
|
||||
}
|
||||
|
||||
pemCerts, err := ioutil.ReadFile(tempPemFile.Name())
|
||||
if err != nil {
|
||||
_ = os.Remove(tempPemFile.Name())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pemBlock, _ := pem.Decode(pemCerts)
|
||||
if pemBlock == nil {
|
||||
_ = os.Remove(tempPemFile.Name())
|
||||
return nil, fmt.Errorf("no valid PEM formatted block found")
|
||||
}
|
||||
|
||||
// If the file does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
|
||||
if pemBlock.Type != "CERTIFICATE" {
|
||||
_ = os.Remove(tempPemFile.Name())
|
||||
return nil, fmt.Errorf("certificate %v contains invalid data, and must be created with 'kubectl create secret tls'", name)
|
||||
}
|
||||
|
||||
pemCert, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
_ = os.Remove(tempPemFile.Name())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//Ensure that certificate and private key have a matching public key
|
||||
if _, err := tls.X509KeyPair(cert, key); err != nil {
|
||||
_ = os.Remove(tempPemFile.Name())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cn := sets.NewString(pemCert.Subject.CommonName)
|
||||
for _, dns := range pemCert.DNSNames {
|
||||
if !cn.Has(dns) {
|
||||
cn.Insert(dns)
|
||||
}
|
||||
}
|
||||
|
||||
if len(pemCert.Extensions) > 0 {
|
||||
glog.V(3).Info("parsing ssl certificate extensions")
|
||||
for _, ext := range getExtension(pemCert, oidExtensionSubjectAltName) {
|
||||
dns, _, _, err := parseSANExtension(ext.Value)
|
||||
if err != nil {
|
||||
glog.Warningf("unexpected error parsing certificate extensions: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, dns := range dns {
|
||||
if !cn.Has(dns) {
|
||||
cn.Insert(dns)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = os.Rename(tempPemFile.Name(), pemFileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not move temp pem file %v to destination %v: %v", tempPemFile.Name(), pemFileName, err)
|
||||
}
|
||||
|
||||
if len(ca) > 0 {
|
||||
bundle := x509.NewCertPool()
|
||||
bundle.AppendCertsFromPEM(ca)
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: bundle,
|
||||
}
|
||||
|
||||
_, err := pemCert.Verify(opts)
|
||||
if err != nil {
|
||||
oe := fmt.Sprintf("failed to verify certificate chain: \n\t%s\n", err)
|
||||
return nil, errors.New(oe)
|
||||
}
|
||||
|
||||
caFile, err := os.OpenFile(pemFileName, os.O_RDWR|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open file %v for writing additional CA chains: %v", pemFileName, err)
|
||||
}
|
||||
|
||||
defer caFile.Close()
|
||||
_, err = caFile.Write([]byte("\n"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not append CA to cert file %v: %v", pemFileName, err)
|
||||
}
|
||||
caFile.Write(ca)
|
||||
caFile.Write([]byte("\n"))
|
||||
|
||||
return &ingress.SSLCert{
|
||||
Certificate: pemCert,
|
||||
CAFileName: pemFileName,
|
||||
PemFileName: pemFileName,
|
||||
PemSHA: file.SHA1(pemFileName),
|
||||
CN: cn.List(),
|
||||
ExpireTime: pemCert.NotAfter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
s := &ingress.SSLCert{
|
||||
Certificate: pemCert,
|
||||
PemFileName: pemFileName,
|
||||
PemSHA: file.SHA1(pemFileName),
|
||||
CN: cn.List(),
|
||||
ExpireTime: pemCert.NotAfter,
|
||||
}
|
||||
|
||||
err = fullChainCert(pemFileName, fullChainPemFileName)
|
||||
if err != nil {
|
||||
glog.Errorf("unexpected error generating SSL certificate with full chain: %v", err)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
s.FullChainPemFileName = fullChainPemFileName
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func getExtension(c *x509.Certificate, id asn1.ObjectIdentifier) []pkix.Extension {
|
||||
var exts []pkix.Extension
|
||||
for _, ext := range c.Extensions {
|
||||
if ext.Id.Equal(id) {
|
||||
exts = append(exts, ext)
|
||||
}
|
||||
}
|
||||
return exts
|
||||
}
|
||||
|
||||
func parseSANExtension(value []byte) (dnsNames, emailAddresses []string, ipAddresses []net.IP, err error) {
|
||||
// RFC 5280, 4.2.1.6
|
||||
|
||||
// SubjectAltName ::= GeneralNames
|
||||
//
|
||||
// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
|
||||
//
|
||||
// GeneralName ::= CHOICE {
|
||||
// otherName [0] OtherName,
|
||||
// rfc822Name [1] IA5String,
|
||||
// dNSName [2] IA5String,
|
||||
// x400Address [3] ORAddress,
|
||||
// directoryName [4] Name,
|
||||
// ediPartyName [5] EDIPartyName,
|
||||
// uniformResourceIdentifier [6] IA5String,
|
||||
// iPAddress [7] OCTET STRING,
|
||||
// registeredID [8] OBJECT IDENTIFIER }
|
||||
var seq asn1.RawValue
|
||||
var rest []byte
|
||||
if rest, err = asn1.Unmarshal(value, &seq); err != nil {
|
||||
return
|
||||
} else if len(rest) != 0 {
|
||||
err = errors.New("x509: trailing data after X.509 extension")
|
||||
return
|
||||
}
|
||||
if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 {
|
||||
err = asn1.StructuralError{Msg: "bad SAN sequence"}
|
||||
return
|
||||
}
|
||||
|
||||
rest = seq.Bytes
|
||||
for len(rest) > 0 {
|
||||
var v asn1.RawValue
|
||||
rest, err = asn1.Unmarshal(rest, &v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch v.Tag {
|
||||
case 1:
|
||||
emailAddresses = append(emailAddresses, string(v.Bytes))
|
||||
case 2:
|
||||
dnsNames = append(dnsNames, string(v.Bytes))
|
||||
case 7:
|
||||
switch len(v.Bytes) {
|
||||
case net.IPv4len, net.IPv6len:
|
||||
ipAddresses = append(ipAddresses, v.Bytes)
|
||||
default:
|
||||
err = errors.New("x509: certificate contained IP address of length " + strconv.Itoa(len(v.Bytes)))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddCertAuth creates a .pem file with the specified CAs to be used in Cert Authentication
|
||||
// If it's already exists, it's clobbered.
|
||||
func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) {
|
||||
|
||||
caName := fmt.Sprintf("ca-%v.pem", name)
|
||||
caFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, caName)
|
||||
|
||||
pemCABlock, _ := pem.Decode(ca)
|
||||
if pemCABlock == nil {
|
||||
return nil, fmt.Errorf("no valid PEM formatted block found")
|
||||
}
|
||||
// If the first certificate does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
|
||||
if pemCABlock.Type != "CERTIFICATE" {
|
||||
return nil, fmt.Errorf("CA file %v contains invalid data, and must be created only with PEM formated certificates", name)
|
||||
}
|
||||
|
||||
_, err := x509.ParseCertificate(pemCABlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(caFileName, ca, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not write CA file %v: %v", caFileName, err)
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Created CA Certificate for Authentication: %v", caFileName)
|
||||
return &ingress.SSLCert{
|
||||
CAFileName: caFileName,
|
||||
PemFileName: caFileName,
|
||||
PemSHA: file.SHA1(caFileName),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AddOrUpdateDHParam creates a dh parameters file with the specified name
|
||||
func AddOrUpdateDHParam(name string, dh []byte) (string, error) {
|
||||
pemName := fmt.Sprintf("%v.pem", name)
|
||||
pemFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, pemName)
|
||||
|
||||
tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName)
|
||||
|
||||
glog.V(3).Infof("Creating temp file %v for DH param: %v", tempPemFile.Name(), pemName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not create temp pem file %v: %v", pemFileName, err)
|
||||
}
|
||||
|
||||
_, err = tempPemFile.Write(dh)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not write to pem file %v: %v", tempPemFile.Name(), err)
|
||||
}
|
||||
|
||||
err = tempPemFile.Close()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not close temp pem file %v: %v", tempPemFile.Name(), err)
|
||||
}
|
||||
|
||||
pemCerts, err := ioutil.ReadFile(tempPemFile.Name())
|
||||
if err != nil {
|
||||
_ = os.Remove(tempPemFile.Name())
|
||||
return "", err
|
||||
}
|
||||
|
||||
pemBlock, _ := pem.Decode(pemCerts)
|
||||
if pemBlock == nil {
|
||||
_ = os.Remove(tempPemFile.Name())
|
||||
return "", fmt.Errorf("no valid PEM formatted block found")
|
||||
}
|
||||
|
||||
// If the file does not start with 'BEGIN DH PARAMETERS' it's invalid and must not be used.
|
||||
if pemBlock.Type != "DH PARAMETERS" {
|
||||
_ = os.Remove(tempPemFile.Name())
|
||||
return "", fmt.Errorf("certificate %v contains invalid data", name)
|
||||
}
|
||||
|
||||
err = os.Rename(tempPemFile.Name(), pemFileName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not move temp pem file %v to destination %v: %v", tempPemFile.Name(), pemFileName, err)
|
||||
}
|
||||
|
||||
return pemFileName, nil
|
||||
}
|
||||
|
||||
// GetFakeSSLCert creates a Self Signed Certificate
|
||||
// Based in the code https://golang.org/src/crypto/tls/generate_cert.go
|
||||
func GetFakeSSLCert() ([]byte, []byte) {
|
||||
|
||||
var priv interface{}
|
||||
var err error
|
||||
|
||||
priv, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||
|
||||
if err != nil {
|
||||
glog.Fatalf("failed to generate fake private key: %s", err)
|
||||
}
|
||||
|
||||
notBefore := time.Now()
|
||||
// This certificate is valid for 365 days
|
||||
notAfter := notBefore.Add(365 * 24 * time.Hour)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
|
||||
if err != nil {
|
||||
glog.Fatalf("failed to generate fake serial number: %s", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Acme Co"},
|
||||
CommonName: "Kubernetes Ingress Controller Fake Certificate",
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{"ingress.local"},
|
||||
}
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.(*rsa.PrivateKey).PublicKey, priv)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to create fake certificate: %s", err)
|
||||
}
|
||||
|
||||
cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
|
||||
key := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv.(*rsa.PrivateKey))})
|
||||
|
||||
return cert, key
|
||||
}
|
||||
|
||||
func fullChainCert(in, out string) error {
|
||||
inputFile, err := os.Open(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(inputFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cert, err := certUtil.DecodeCertificate(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certs, err := certUtil.FetchCertificateChain(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certs, err = certUtil.AddRootCA(certs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data = certUtil.EncodeCertificates(certs)
|
||||
return ioutil.WriteFile(out, data, 0644)
|
||||
}
|
||||
151
pkg/net/ssl/ssl_test.go
Normal file
151
pkg/net/ssl/ssl_test.go
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ssl
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/cert/triple"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/ingress"
|
||||
)
|
||||
|
||||
// generateRSACerts generates a self signed certificate using a self generated ca
|
||||
func generateRSACerts(host string) (*triple.KeyPair, *triple.KeyPair, error) {
|
||||
ca, err := triple.NewCA("self-sign-ca")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create a server private key: %v", err)
|
||||
}
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: host,
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||
}
|
||||
cert, err := certutil.NewSignedCert(config, key, ca.Cert, ca.Key)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to sign the server certificate: %v", err)
|
||||
}
|
||||
|
||||
return &triple.KeyPair{
|
||||
Key: key,
|
||||
Cert: cert,
|
||||
}, ca, nil
|
||||
}
|
||||
|
||||
func TestAddOrUpdateCertAndKey(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "ssl")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error creating temporal directory: %v", err)
|
||||
}
|
||||
ingress.DefaultSSLDirectory = td
|
||||
|
||||
cert, _, err := generateRSACerts("echoheaders")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("test-%v", time.Now().UnixNano())
|
||||
|
||||
c := certutil.EncodeCertPEM(cert.Cert)
|
||||
k := certutil.EncodePrivateKeyPEM(cert.Key)
|
||||
|
||||
ngxCert, err := AddOrUpdateCertAndKey(name, c, k, []byte{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error checking SSL certificate: %v", err)
|
||||
}
|
||||
|
||||
if ngxCert.PemFileName == "" {
|
||||
t.Fatalf("expected path to pem file but returned empty")
|
||||
}
|
||||
|
||||
if len(ngxCert.CN) == 0 {
|
||||
t.Fatalf("expected at least one cname but none returned")
|
||||
}
|
||||
|
||||
if ngxCert.CN[0] != "echoheaders" {
|
||||
t.Fatalf("expected cname echoheaders but %v returned", ngxCert.CN[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCACert(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "ssl")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error creating temporal directory: %v", err)
|
||||
}
|
||||
ingress.DefaultSSLDirectory = td
|
||||
|
||||
cert, CA, err := generateRSACerts("echoheaders")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("test-%v", time.Now().UnixNano())
|
||||
|
||||
c := certutil.EncodeCertPEM(cert.Cert)
|
||||
k := certutil.EncodePrivateKeyPEM(cert.Key)
|
||||
ca := certutil.EncodeCertPEM(CA.Cert)
|
||||
|
||||
ngxCert, err := AddOrUpdateCertAndKey(name, c, k, ca)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error checking SSL certificate: %v", err)
|
||||
}
|
||||
if ngxCert.CAFileName == "" {
|
||||
t.Fatalf("expected a valid CA file name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFakeSSLCert(t *testing.T) {
|
||||
k, c := GetFakeSSLCert()
|
||||
if len(k) == 0 {
|
||||
t.Fatalf("expected a valid key")
|
||||
}
|
||||
if len(c) == 0 {
|
||||
t.Fatalf("expected a valid certificate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddCertAuth(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "ssl")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error creating temporal directory: %v", err)
|
||||
}
|
||||
ingress.DefaultSSLDirectory = td
|
||||
|
||||
cn := "demo-ca"
|
||||
_, ca, err := generateRSACerts(cn)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
}
|
||||
c := certutil.EncodeCertPEM(ca.Cert)
|
||||
ic, err := AddCertAuth(cn, c)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
}
|
||||
if ic.CAFileName == "" {
|
||||
t.Fatalf("expected a valid CA file name")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue