@@ -3,9 +3,16 @@ package store
33import (
44 "bytes"
55 "context"
6+ "crypto/ecdsa"
7+ "crypto/elliptic"
8+ "crypto/rand"
9+ "crypto/x509"
10+ "crypto/x509/pkix"
11+ "encoding/pem"
612 "errors"
713 "fmt"
814 "io"
15+ "math/big"
916 "net"
1017 "net/http"
1118 "net/http/httptest"
@@ -252,7 +259,7 @@ func TestRewriteReference(t *testing.T) {
252259 seedImage (t , host , "src/repo" , "v1" , rOpts ... )
253260
254261 s := newTestStore (t )
255- if _ , err := s .AddImage (ctx , host + "/src/repo:v1" , "" , false , "" , rOpts ... ); err != nil {
262+ if _ , err := s .AddImage (ctx , host + "/src/repo:v1" , "" , false , "" , false , "" , rOpts ... ); err != nil {
256263 t .Fatalf ("AddImage: %v" , err )
257264 }
258265
@@ -354,7 +361,7 @@ func TestRewriteReference(t *testing.T) {
354361 seedImage (t , host , "src/repo" , "v1" , rOpts ... )
355362
356363 s := newTestStore (t )
357- if _ , err := s .AddImage (ctx , host + "/src/repo:v1" , "" , false , "" , rOpts ... ); err != nil {
364+ if _ , err := s .AddImage (ctx , host + "/src/repo:v1" , "" , false , "" , false , "" , rOpts ... ); err != nil {
358365 t .Fatalf ("AddImage: %v" , err )
359366 }
360367
@@ -2148,6 +2155,7 @@ func TestResolveChartJobs_NoCharts(t *testing.T) {
21482155// TestResolveChartJobs_CredentialFields pins that every TLS/verification
21492156// field on v1.Chart reaches the job's ChartOpts unchanged.
21502157func TestResolveChartJobs_CredentialFields (t * testing.T ) {
2158+ insecure := true
21512159 ch := v1.Chart {
21522160 Name : "rancher" ,
21532161 Verify : true ,
@@ -2156,7 +2164,7 @@ func TestResolveChartJobs_CredentialFields(t *testing.T) {
21562164 CertFile : "/certs/client.crt" ,
21572165 KeyFile : "/certs/client.key" ,
21582166 CaFile : "/certs/ca.crt" ,
2159- InsecureSkipTLSVerify : true ,
2167+ InsecureSkipTLSVerify : & insecure ,
21602168 PlainHTTP : true ,
21612169 }
21622170
@@ -2187,8 +2195,8 @@ func TestResolveChartJobs_CredentialFields(t *testing.T) {
21872195 if opts .CaFile != ch .CaFile {
21882196 t .Errorf ("CaFile = %q, want %q" , opts .CaFile , ch .CaFile )
21892197 }
2190- if opts .InsecureSkipTLSVerify != ch .InsecureSkipTLSVerify {
2191- t .Errorf ("InsecureSkipTLSVerify = %v, want %v" , opts .InsecureSkipTLSVerify , ch .InsecureSkipTLSVerify )
2198+ if opts .InsecureSkipTLSVerify != derefInsecure ( ch .InsecureSkipTLSVerify ) {
2199+ t .Errorf ("InsecureSkipTLSVerify = %v, want %v" , opts .InsecureSkipTLSVerify , derefInsecure ( ch .InsecureSkipTLSVerify ) )
21922200 }
21932201 if opts .PlainHTTP != ch .PlainHTTP {
21942202 t .Errorf ("PlainHTTP = %v, want %v" , opts .PlainHTTP , ch .PlainHTTP )
@@ -2972,3 +2980,100 @@ func TestFormatAddedLine_WithStats(t *testing.T) {
29722980 })
29732981 }
29742982}
2983+
2984+ // TestStoreImage_CAFileAndInsecure exercises the insecureSkipTLSVerify / caFile
2985+ // plumbing through storeImage -> AddImage. Note: the in-memory registry runs on
2986+ // localhost, which go-containerregistry forces to http, so these cases do NOT
2987+ // perform a real TLS handshake — the actual CA trust/reject behavior is covered
2988+ // by buildTransport's handshake test in pkg/store. What's verified here is caFile
2989+ // error propagation, insecure-over-caFile precedence, and that a valid caFile
2990+ // doesn't break the pull.
2991+ func TestStoreImage_CAFileAndInsecure (t * testing.T ) {
2992+ ctx := newTestContext (t )
2993+ host , rOpts := newLocalhostRegistry (t )
2994+ seedImage (t , host , "tls/repo" , "v1" , rOpts ... )
2995+ ref := host + "/tls/repo:v1"
2996+
2997+ const missingCA = "/nonexistent/ca.pem"
2998+
2999+ t .Run ("bad caFile without insecure returns error and stores nothing" , func (t * testing.T ) {
3000+ s := newTestStore (t )
3001+ insecure := false
3002+ img := v1.Image {Name : ref , CaFile : missingCA , InsecureSkipTLSVerify : & insecure }
3003+ err := storeImage (ctx , s , img , "" , false ,
3004+ defaultRootOpts (s .Root ), defaultCliOpts (), "" , "" , false )
3005+ if err == nil {
3006+ t .Fatal ("expected error from unreadable caFile, got nil" )
3007+ }
3008+ if n := countArtifactsInStore (t , s ); n != 0 {
3009+ t .Errorf ("expected nothing stored on caFile error, got %d" , n )
3010+ }
3011+ })
3012+
3013+ t .Run ("non-PEM caFile without insecure returns error" , func (t * testing.T ) {
3014+ s := newTestStore (t )
3015+ junk := filepath .Join (t .TempDir (), "junk.pem" )
3016+ if err := os .WriteFile (junk , []byte ("not a certificate" ), 0o600 ); err != nil {
3017+ t .Fatal (err )
3018+ }
3019+ insecure := false
3020+ img := v1.Image {Name : ref , CaFile : junk , InsecureSkipTLSVerify : & insecure }
3021+ err := storeImage (ctx , s , img , "" , false ,
3022+ defaultRootOpts (s .Root ), defaultCliOpts (), "" , "" , false )
3023+ if err == nil {
3024+ t .Fatal ("expected error from non-PEM caFile, got nil" )
3025+ }
3026+ })
3027+
3028+ t .Run ("insecure takes precedence over bad caFile" , func (t * testing.T ) {
3029+ s := newTestStore (t )
3030+ // insecure=true short-circuits before caFile is read; the bogus path is
3031+ // ignored and the pull still succeeds. If caFile were read first, the
3032+ // pull would error and nothing would be stored.
3033+ insecure := true
3034+ img := v1.Image {Name : ref , CaFile : missingCA , InsecureSkipTLSVerify : & insecure }
3035+ err := storeImage (ctx , s , img , "" , false ,
3036+ defaultRootOpts (s .Root ), defaultCliOpts (), "" , "" , false )
3037+ if err != nil {
3038+ t .Fatalf ("insecure should ignore caFile, got: %v" , err )
3039+ }
3040+ assertArtifactInStore (t , s , "tls/repo:v1" )
3041+ })
3042+
3043+ t .Run ("valid caFile without insecure is accepted" , func (t * testing.T ) {
3044+ s := newTestStore (t )
3045+ insecure := false
3046+ img := v1.Image {Name : ref , CaFile : writeCAFile (t ), InsecureSkipTLSVerify : & insecure }
3047+ err := storeImage (ctx , s , img , "" , false ,
3048+ defaultRootOpts (s .Root ), defaultCliOpts (), "" , "" , false )
3049+ if err != nil {
3050+ t .Fatalf ("valid caFile should be accepted, got: %v" , err )
3051+ }
3052+ assertArtifactInStore (t , s , "tls/repo:v1" )
3053+ })
3054+ }
3055+
3056+ // writeCAFile writes a valid self-signed cert PEM to a temp file and returns its
3057+ // path. Its only job is to be a parseable CA file (AppendCertsFromPEM succeeds).
3058+ func writeCAFile (t * testing.T ) string {
3059+ t .Helper ()
3060+ key , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
3061+ if err != nil {
3062+ t .Fatal (err )
3063+ }
3064+ tmpl := & x509.Certificate {
3065+ SerialNumber : big .NewInt (1 ),
3066+ Subject : pkix.Name {CommonName : "test-ca" },
3067+ NotBefore : time .Now ().Add (- time .Hour ),
3068+ NotAfter : time .Now ().Add (time .Hour ),
3069+ }
3070+ der , err := x509 .CreateCertificate (rand .Reader , tmpl , tmpl , & key .PublicKey , key )
3071+ if err != nil {
3072+ t .Fatal (err )
3073+ }
3074+ p := filepath .Join (t .TempDir (), "ca.pem" )
3075+ if err := os .WriteFile (p , pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : der }), 0o600 ); err != nil {
3076+ t .Fatal (err )
3077+ }
3078+ return p
3079+ }
0 commit comments