Skip to content

Commit

Permalink
Support certificate authority configuration (#380)
Browse files Browse the repository at this point in the history
Signed-off-by: Billy Zha <[email protected]>
  • Loading branch information
qweeah authored May 16, 2022
1 parent 92c2374 commit 5f6a1e7
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 10 deletions.
25 changes: 16 additions & 9 deletions cmd/oras/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ type loginOptions struct {
hostname string
fromStdin bool

debug bool
configs []string
username string
password string
insecure bool
plainHttp bool
verbose bool
debug bool
configs []string
caFilePath string
username string
password string
insecure bool
plainHTTP bool
verbose bool
}

func loginCmd() *cobra.Command {
Expand Down Expand Up @@ -85,7 +86,8 @@ Example - Login with insecure registry from command line:
cmd.Flags().StringVarP(&opts.password, "password", "p", "", "registry password or identity token")
cmd.Flags().BoolVarP(&opts.fromStdin, "password-stdin", "", false, "read password or identity token from stdin")
cmd.Flags().BoolVarP(&opts.insecure, "insecure", "k", false, "allow connections to SSL registry without certs")
cmd.Flags().BoolVarP(&opts.plainHttp, "plain-http", "", false, "allow insecure connections to registry without SSL")
cmd.Flags().StringVarP(&opts.caFilePath, "ca-file", "", "", "server certificate authority file for the remote registry")
cmd.Flags().BoolVarP(&opts.plainHTTP, "plain-http", "", false, "allow insecure connections to registry without SSL")
cmd.Flags().BoolVarP(&opts.verbose, "verbose", "v", false, "verbose output")
return cmd
}
Expand Down Expand Up @@ -145,12 +147,17 @@ func runLogin(opts loginOptions) (err error) {
if err != nil {
return err
}
remote.PlainHTTP = opts.plainHttp
remote.PlainHTTP = opts.plainHTTP
cred := credential.Credential(opts.username, opts.password)
rootCAs, err := http.LoadCertPool(opts.caFilePath)
if err != nil {
return err
}
remote.Client = http.NewClient(http.ClientOptions{
Credential: cred,
SkipTLSVerify: opts.insecure,
Debug: opts.debug,
RootCAs: rootCAs,
})
if err = remote.Ping(ctx); err != nil {
return err
Expand Down
19 changes: 19 additions & 0 deletions internal/http/certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package http

import (
"crypto/x509"
"errors"
"os"
)

func LoadCertPool(path string) (*x509.CertPool, error) {
pool := x509.NewCertPool()
pemBytes, err := os.ReadFile(path)
if err != nil {
return nil, err
}
if ok := pool.AppendCertsFromPEM(pemBytes); !ok {
return nil, errors.New("Failed to load certificate in file: " + path)
}
return pool, nil
}
3 changes: 3 additions & 0 deletions internal/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package http
import (
"context"
"crypto/tls"
"crypto/x509"
"net/http"

"oras.land/oras-go/v2/registry/remote"
Expand All @@ -18,6 +19,7 @@ type ClientOptions struct {
CredentialStore *credential.Store
SkipTLSVerify bool
Debug bool
RootCAs *x509.CertPool
}

func NewClient(opts ClientOptions) remote.Client {
Expand All @@ -26,6 +28,7 @@ func NewClient(opts ClientOptions) remote.Client {
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: opts.SkipTLSVerify,
RootCAs: opts.RootCAs,
},
},
},
Expand Down
35 changes: 34 additions & 1 deletion internal/http/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ limitations under the License.
package http_test

import (
"context"
"crypto/x509"
"testing"

nhttp "net/http"
"net/http/httptest"

"oras.land/oras-go/v2/registry/remote/auth"
"oras.land/oras/internal/http"
Expand All @@ -41,7 +44,7 @@ func Test_NewClient_credential(t *testing.T) {
}
}

func Test_NewClient_tlsConfig(t *testing.T) {
func Test_NewClient_skipTlsVerify(t *testing.T) {
opts := http.ClientOptions{
SkipTLSVerify: true,
}
Expand All @@ -54,3 +57,33 @@ func Test_NewClient_tlsConfig(t *testing.T) {
t.Fatalf("expect: %v, got: %v", wanted, got)
}
}

func Test_NewClient_CARoots(t *testing.T) {
// Test server
ts := httptest.NewTLSServer(nhttp.HandlerFunc(func(w nhttp.ResponseWriter, r *nhttp.Request) {
p := r.URL.Path
m := r.Method
switch {
case p == "/v2/" && m == "GET":
w.WriteHeader(nhttp.StatusOK)
}
}))
defer ts.Close()

// Test CA pool
pool := x509.NewCertPool()
pool.AddCert(ts.Certificate())
opts := http.ClientOptions{
RootCAs: pool,
}

client := http.NewClient(opts)
req, err := nhttp.NewRequestWithContext(context.Background(), nhttp.MethodGet, ts.URL, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
_, err = client.Do(req)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}

0 comments on commit 5f6a1e7

Please sign in to comment.