diff --git a/client/client.crt b/client/client.crt deleted file mode 100644 index d72183140..000000000 --- a/client/client.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC/zCCAeegAwIBAgIJAMFY0fX2HnxSMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNV -BAMMCkFCQ0QtMTIzNDUwIBcNMTYwMzAzMjMzNDU3WhgPMjEwMzEwMTQyMzM0NTda -MBUxEzARBgNVBAMMCkFCQ0QtMTIzNDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDLuSiZ1VYYf0Cy7trBPkTbHxuqShzJDYrbJ+ZOrymB+iPhFBqZA0yg -2YdRz5vMvDLLqqCqZyBqvM1SXz8njPahBKbOCXlxeGVuZuw+P12ACIXB4/axbIcK -4t3fz55nD6n+FUXqMhnrDN0eHqUiLiBoskxlr5MRGgP0wYybkqzlQA15CTp9AK+v -vOLcZyE3Fj2Q08POf/TgRW061d7zemszR20iQJiRyqM5xpqsF2Vp5KJwS0Mp5M0a -DTMZhDmZEkBgVsVf0FmtVuw3b+aEZq/5DdAn9ybZndiNm5raAvaelIj+hMnZ0Sbh -NnhclVvs3KHbVbd/YGDaVj9jnTYDugkpAgMBAAGjUDBOMB0GA1UdDgQWBBQrOpzB -BxLjWtjoRB71k4tMPYROqjAfBgNVHSMEGDAWgBQrOpzBBxLjWtjoRB71k4tMPYRO -qjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA6zFWZcs0WEez3DvT5 -KVymxWBt5zAYM0D619gZgZuleDLrQ2a0sPPWBrLFRywbp1kd1mOokMrfMcM9i7Eg -ziwuMbTgkcDuyOoTk1kY5mzjPHTqXzErzxjumFiQdvExf8TjkjNTy7fUunouW6QZ -YG7jZ5gf/ad/nkZVjKOym4UzIv23v0O3+3x5+q0DBmulZIanpn6qIJ9O9IRKJSO8 -U1b9TxM7VRrx2iIQ0lw6anqe0gFH0RiCzN8Zn7MEwEmVWXFLCFEZWwRukpeo8/n/ -u/qD3/Zjqk3pVa3r/988JDnom/phiJjzAAeIA3CNTUqLyK9TnFS5ZHH5f2/7Z0UC -PECQ ------END CERTIFICATE----- diff --git a/client/client.go b/client/client.go index b86afdf27..51f2f71f3 100644 --- a/client/client.go +++ b/client/client.go @@ -16,6 +16,7 @@ package client import ( "crypto/tls" "crypto/x509" + "encoding/pem" "fmt" "io/ioutil" "net" @@ -187,11 +188,24 @@ func loadServerTrust(conf Config) (*x509.CertPool, error) { // Read certificate file. servcert, err := ioutil.ReadFile(conf.ServerCert) if err != nil { + log.Errorf("%s is inaccessible: %s", conf.ServerCert, err.Error()) return nil, err } if len(servcert) == 0 { - return nil, errors.New("unable to find system and server certificates") + log.Errorf("Both %s and the system certificate pool are empty.", + conf.ServerCert) + return nil, errors.New("server certificate is empty") + } + + block, _ := pem.Decode([]byte(servcert)) + if block != nil { + cert, err := x509.ParseCertificate(block.Bytes) + if err == nil { + log.Infof("API Gateway certificate (in PEM format): \n%s", string(servcert)) + log.Infof("Issuer: %s, Valid from: %s, Valid to: %s", + cert.Issuer.Organization, cert.NotBefore, cert.NotAfter) + } } if syscerts == nil { diff --git a/client/client.key b/client/client.key deleted file mode 100644 index 345fc4752..000000000 --- a/client/client.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDLuSiZ1VYYf0Cy -7trBPkTbHxuqShzJDYrbJ+ZOrymB+iPhFBqZA0yg2YdRz5vMvDLLqqCqZyBqvM1S -Xz8njPahBKbOCXlxeGVuZuw+P12ACIXB4/axbIcK4t3fz55nD6n+FUXqMhnrDN0e -HqUiLiBoskxlr5MRGgP0wYybkqzlQA15CTp9AK+vvOLcZyE3Fj2Q08POf/TgRW06 -1d7zemszR20iQJiRyqM5xpqsF2Vp5KJwS0Mp5M0aDTMZhDmZEkBgVsVf0FmtVuw3 -b+aEZq/5DdAn9ybZndiNm5raAvaelIj+hMnZ0SbhNnhclVvs3KHbVbd/YGDaVj9j -nTYDugkpAgMBAAECggEBAJUzJN4NFQHZ6ItjTQirzwffr4Y2wwAoD+LTROOpey+6 -/53u+E/Jz8wZo/Yxovv9dQIeE47Y+i/R2FmUcg9IOLPSUUuiOrP0peyGKkyoYZTM -KRHIH7rvLbO2VSJ9zpfSLABumgab7MltYV+Wt//hYlDrC/KI9J9Yuz78pTqm4EMA -KBNM0ZOR7LijIPG1rgXwxcw+Jw6xqmSRiNTjd2IiMusw4dvoqf6L6j7UnRUL7Tdi -uOxjN4yo65u/Skozke9N3ANyyEjuwXW/5+4NUdsvgauwWyVglKQLfSyA1TqLe9De -sqgBb+gQ9fnbm3wdV3hMMmTKlWcazLKO7NJz2x078AECgYEA8adT16PSpQwGqnPL -kk9sURl89+mGROy0GQoVOZwntwEhrmqyc2O1hMEViXuh0N3ORugHBt7s4sw4SPjD -Zk03BV1B5GyqfPMqy+t2RekP4AkfOmNTYHO5Ig6GvNKhP+y+NtbKdN2OZxJa/Rxm -5j1dJs+KX1hNf2Zefe3AFMREGykCgYEA19Fcx1EKaT69rzNu2PcFPigSAwudKIy6 -ymiuBO2Jj3Scf7INrzQZb2ETL4zi6IpGMup0CULPjHMDYvhfbpC7Ka5qgGFEPZ2t -j1lqJD0ulMfOgQAQRxXcbbPbrQKrE4kyfpoqoKFOch0NFoq5ybgpYCYBCuw8q8dF -YT6PixgSPgECgYBNPrSBSL+rvHVhBRS8lyDDp8cXpBEkdRl4q41vVegoCneOvlJ+ -qg+6+NF+mgN2tYAbkE22t4m3UZpoUQR87g+5aj8G2mmA+WXju0eaP06wyZmu21Mq -kLNMHKKbNyUOPwnqk6wxwSSk8+wb3nlR/amKH2tW4O4c3Ec2L8pnmPlASQKBgQCz -pmnO2N0LwthvMSdCoE3S8dQciNxn1s3ek8jX/UuXCQEFIxhVEc9icbJg0KxJ2mCZ -HaU92EmgplTZW9tE1GGYTsIE3/LIP6ssOsgqJmvSGYkCxSTHp6D4CNICcMgr2PcZ -gAef8ua0Aw7UBuKk0hJPIROZHhFbBOPyEPu4U29MAQKBgQCzRfkF9Rcuh5bgFj0W -6OaItlxPQorVdkscDEMdt4XhM/kCFsQ1GQDWkFsDUX99TDwCE4Lwr8CDQK9vf+xz -Dojwhjscn76Uxlp3IevuQxeT2GVQh4UIzc7KsmetJnA/1G4u0PrFqPQsuAPF4+jA -WZCrFEmvcVwRcEHnK2Zms93/yw== ------END PRIVATE KEY----- diff --git a/client/client_auth.go b/client/client_auth.go index c85429b08..677fe3b1a 100644 --- a/client/client_auth.go +++ b/client/client_auth.go @@ -15,10 +15,13 @@ package client import ( "bytes" + "crypto/x509" "encoding/base64" "fmt" "io/ioutil" "net/http" + "net/url" + "time" "github.com/mendersoftware/log" "github.com/pkg/errors" @@ -49,7 +52,42 @@ func (u *AuthClient) Request(api ApiRequester, server string, dataSrc AuthDataMe log.Debugf("making authorization request to server %s with req: %s", server, req) rsp, err := api.Do(req) if err != nil { - return nil, errors.Wrapf(err, "failed to execute authorization request") + log.Errorf("Failure occured while executing authorization request: %#v", err) + + // checking the detailed reason of the failure + if urlErr, ok := err.(*url.Error); ok { + switch certErr := urlErr.Err.(type) { + case x509.UnknownAuthorityError: + log.Error("Certificate is signed by unknown authority.") + log.Error("If you are using a self-signed certificate, make sure it is " + + "available locally to the Mender client in /etc/mender/server.crt and " + + "is configured properly in /etc/mender/mender.conf.") + log.Error("See https://docs.mender.io/troubleshooting/mender-client#" + + "certificate-signed-by-unknown-authority for more information.") + + return nil, errors.Wrapf(err, "certificate signed by unknown authority") + + case x509.CertificateInvalidError: + switch certErr.Reason { + case x509.Expired: + log.Error("Certificate has expired or is not yet valid.") + log.Errorf("Current clock is %s", time.Now()) + log.Error("Verify that the clock on the device is correct " + + "and/or certificate expiration date is valid.") + log.Error("See https://docs.mender.io/troubleshooting/mender-client#" + + "certificate-expired-or-not-yet-valid for more information.") + + return nil, errors.Wrapf(err, "certificate has expired") + default: + log.Errorf("Server certificate is invalid, reason: %#v", certErr.Reason) + } + return nil, errors.Wrapf(err, "certificate exists, but is invalid") + default: + log.Errorf("authorization request error: %v", certErr) + } + } + return nil, errors.Wrapf(err, + "generic error occured while executing authorization request") } defer rsp.Body.Close() diff --git a/client/client_auth_test.go b/client/client_auth_test.go index 6f07cac62..dd39b6755 100644 --- a/client/client_auth_test.go +++ b/client/client_auth_test.go @@ -120,3 +120,62 @@ func TestClientAuth(t *testing.T) { _, err = client.Request(ac, ts.URL, msger) assert.Error(t, err) } + +func TestClientAuthExpiredCert(t *testing.T) { + ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + })) + defer ts.Close() + + ac, err := NewApiClient( + Config{"server.expired.crt", true, false}, + ) + assert.NotNil(t, ac) + assert.NoError(t, err) + + client := NewAuth() + assert.NotNil(t, client) + + msger := &testAuthDataMessenger{ + reqData: []byte("foobar"), + } + rsp, err := client.Request(ac, ts.URL, msger) + assert.Error(t, err) + assert.Contains(t, err.Error(), "certificate has expired") + assert.Nil(t, rsp) +} + +func TestClientAuthUnknownAuthorityCert(t *testing.T) { + ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + })) + defer ts.Close() + + ac, err := NewApiClient( + Config{"server.unknown-authority.crt", true, false}, + ) + assert.NotNil(t, ac) + assert.NoError(t, err) + + client := NewAuth() + assert.NotNil(t, client) + + msger := &testAuthDataMessenger{ + reqData: []byte("foobar"), + } + rsp, err := client.Request(ac, ts.URL, msger) + assert.Error(t, err) + assert.Contains(t, err.Error(), "certificate signed by unknown authority") + assert.Nil(t, rsp) +} + +func TestClientAuthNoCert(t *testing.T) { + ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + })) + defer ts.Close() + + ac, err := NewApiClient( + Config{"server.non-existing.crt", true, false}, + ) + assert.Nil(t, ac) + assert.Error(t, err) + assert.Contains(t, err.Error(), "cannot initialize server trust") +} diff --git a/client/server.expired.crt b/client/server.expired.crt new file mode 100644 index 000000000..31112b398 --- /dev/null +++ b/client/server.expired.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBmzCCAQQCCQDMKfSEuawBWTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQKEwdB +Y21lIENvMB4XDTcxMDEwMTEzMDMxNloXDTcyMDEwMTEzMDMxNlowEjEQMA4GA1UE +ChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA7i50ACN5g4Hs +t1Qc1pwdi9/SVFOLcY3lROpsrg6pDP7hpX42/Uo2NWyMOIgObAQWcx4t89IdWFfE +D7sRG4uJ1vdSCZtoLL72YSKwh0t5emn2m+KWQZdvl7A7paygcSirQ9axsZRuC453 +HWmxZ/YLy2Wndm0srKs/6kwFErU76iUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAi +K3rBgk+rMIfXtfWO7naaob/b4ASfUbMAwc1J+wOqXOj9O9wf/5xD5O8/pEenmp5k +M3nPb2xzRhRbusFFG0fZebk2U7DUf+JjN8pdiqfW3gDfOQHh3IdeGrLRsZ3A/K7z +j6GZ2oBCoVjobmbA713gcMJggzzBJbCPmGpFjKYHOQ== +-----END CERTIFICATE----- diff --git a/client/server.unknown-authority.crt b/client/server.unknown-authority.crt new file mode 100644 index 000000000..903d568f2 --- /dev/null +++ b/client/server.unknown-authority.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICEjCCAXugAwIBAgIRAMQ8I/xATeFlYDeiX3H1XBkwDQYJKoZIhvcNAQELBQAw +EjEQMA4GA1UEChMHQWNtZSBDbzAeFw03MDAxMDEwMDAwMDBaFw03MDAxMDMwMDAw +MDBaMBIxEDAOBgNVBAoTB0FjbWUgQ28wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ +AoGBAJiCLkrvs3bels/qVWe5SojBygTrU7D1Qn/dZ7ZSJF1svUuvwDq8p6wHC1y5 +tX+KBjOjxtUjUdIQgfuCjAbw2j7hgrpEenieQpVTccb44aRa1ufobjKYhWiIZ+K6 +96udAaoSd1tt+TJpU2Ym2gWzWBMSpGAqKdgrmFfFMl/9mHKlAgMBAAGjaDBmMA4G +A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD +AQH/MC4GA1UdEQQnMCWCC2V4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAAAAAA +AAABMA0GCSqGSIb3DQEBCwUAA4GBAH0gC0D1t1fZ+Nv5OWfve1n435EQ/eLOu0NA +AZ3LjRZjByytlBEvdO8F+xVeLhm/924B2G0VItotsg4888R7AZ43TceBFN4LvPDt +iRMfWmk3Z87fOiMADOFlxdCvA5ceA2o1VDnOblcIf8czSaVTXq9vrmCNhE8P0WU0 +L/r4D5nk +-----END CERTIFICATE-----