Skip to content

Commit

Permalink
ostree: add plain MTLS support
Browse files Browse the repository at this point in the history
  • Loading branch information
lzap committed Oct 29, 2024
1 parent 2404ea8 commit c67d181
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 42 deletions.
6 changes: 5 additions & 1 deletion cmd/otk/osbuild-resolve-ostree-commit/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ func run(r io.Reader, w io.Writer) error {
return err
}

sourceSpec := ostree.SourceSpec(inputTree.Tree)
sourceSpec := ostree.SourceSpec{
URL: inputTree.Tree.URL,
Ref: inputTree.Tree.Ref,
RHSM: inputTree.Tree.RHSM,
}

var commitSpec ostree.CommitSpec
if !underTest() {
Expand Down
105 changes: 67 additions & 38 deletions pkg/ostree/ostree.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,21 @@ var (
// SourceSpec serves as input for ResolveParams, and contains all necessary
// variables to resolve a ref, which can then be turned into a CommitSpec.
type SourceSpec struct {
URL string
Ref string
URL string
Ref string
// RHSM indicates to use RHSM secrets when pulling from the remote. Alternatively, you can use MTLS with plain certs.
RHSM bool
// MTLS information. Will be ignored if RHSM is set.
MTLS *MTLS
// Proxy as HTTP proxy to use when fetching the ref.
Proxy string
}

// MTLS contains the options for resolving an ostree source.
type MTLS struct {
CA string
ClientCert string
ClientKey string
}

// CommitSpec specifies an ostree commit using any combination of Ref (branch), URL (source), and Checksum (commit ID).
Expand Down Expand Up @@ -138,59 +150,53 @@ func verifyChecksum(commit string) bool {
return len(commit) > 0 && ostreeCommitRE.MatchString(commit)
}

// ResolveRef resolves the URL path specified by the location and ref
// resolveRef resolves the URL path specified by the location and ref
// (location+"refs/heads/"+ref) and returns the commit ID for the named ref. If
// there is an error, it will be of type ResolveRefError.
func resolveRef(location, ref string, consumerCerts bool, subs *rhsm.Subscriptions, ca *string) (string, error) {
u, err := url.Parse(location)
func resolveRef(ss SourceSpec) (string, error) {
u, err := url.Parse(ss.URL)
if err != nil {
return "", NewResolveRefError("error parsing ostree repository location: %v", err)
}
u.Path = path.Join(u.Path, "refs/heads/", ref)

var client *http.Client
if consumerCerts {
if subs == nil {
subs, err = rhsm.LoadSystemSubscriptions()
if err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref: %s", err)
}
if subs.Consumer == nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
}
}
u.Path = path.Join(u.Path, "refs/heads/", ss.Ref)

transport := http.DefaultTransport.(*http.Transport).Clone()
client := &http.Client{
Transport: transport,
Timeout: 300 * time.Second,
}
if u.Scheme == "https" {
tlsConf := &tls.Config{
MinVersion: tls.VersionTLS12,
}

if ca != nil {
caCertPEM, err := os.ReadFile(*ca)
// If CA is set, load the CA certificate and add it to the TLS configuration. Otherwise, use the system CA.
if ss.MTLS.CA != "" {
caCertPEM, err := os.ReadFile(ss.MTLS.CA)
if err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref: %s", err)
return "", NewResolveRefError("error adding ca certificate when resolving ref: %s", err)
}
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM(caCertPEM)
if !ok {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
tlsConf.RootCAs = x509.NewCertPool()
if ok := tlsConf.RootCAs.AppendCertsFromPEM(caCertPEM); !ok {
return "", NewResolveRefError("error adding ca certificate when resolving ref")
}
tlsConf.RootCAs = roots
}

cert, err := tls.LoadX509KeyPair(subs.Consumer.ConsumerCert, subs.Consumer.ConsumerKey)
if err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref: %s", err)
if ss.MTLS.ClientCert != "" && ss.MTLS.ClientKey != "" {
cert, err := tls.LoadX509KeyPair(ss.MTLS.ClientCert, ss.MTLS.ClientKey)
if err != nil {
return "", NewResolveRefError("error adding client certificate when resolving ref: %s", err)
}
tlsConf.Certificates = []tls.Certificate{cert}
}
tlsConf.Certificates = []tls.Certificate{cert}

client = &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConf,
},
Timeout: 300 * time.Second,
transport.TLSClientConfig = tlsConf
}

if ss.Proxy != "" {
transport.Proxy = func(request *http.Request) (*url.URL, error) {
return url.Parse(ss.Proxy)
}
} else {
client = &http.Client{}
}

req, err := http.NewRequest(http.MethodGet, u.String(), nil)
Expand Down Expand Up @@ -234,8 +240,31 @@ func Resolve(source SourceSpec) (CommitSpec, error) {
URL: source.URL,
}

if source.RHSM && source.MTLS != nil {
return commit, NewResolveRefError("cannot use both RHSM and MTLS when resolving ref")
}

if source.RHSM {
var subs *rhsm.Subscriptions
var err error

commit.Secrets = "org.osbuild.rhsm.consumer"
subs, err = rhsm.LoadSystemSubscriptions()

if err != nil {
return commit, NewResolveRefError("error adding rhsm certificates when resolving ref: %s", err)
}

if subs.Consumer == nil {
return commit, NewResolveRefError("error adding rhsm certificates when resolving ref")
}

source.MTLS = &MTLS{
ClientCert: subs.Consumer.ConsumerCert,
ClientKey: subs.Consumer.ConsumerKey,
}
} else if source.MTLS != nil {
commit.Secrets = "org.osbuild.mtls"
}

if verifyChecksum(source.Ref) {
Expand All @@ -252,7 +281,7 @@ func Resolve(source SourceSpec) (CommitSpec, error) {
// URL set: Resolve checksum
if source.URL != "" {
// If a URL is specified, we need to fetch the commit at the URL.
checksum, err := resolveRef(source.URL, source.Ref, source.RHSM, nil, nil)
checksum, err := resolveRef(source)
if err != nil {
return CommitSpec{}, err // ResolveRefError
}
Expand Down
18 changes: 15 additions & 3 deletions pkg/ostree/ostree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/osbuild/images/pkg/rhsm"
)

func TestOstreeresolveRef(t *testing.T) {
func TestOstreeResolveRef(t *testing.T) {
goodRef := "5330bb1b8820944567f519de66ad6354c729b6b490dea1c5a7ba320c9f147c58"
badRef := "<html>not a ref</html>"

Expand Down Expand Up @@ -78,7 +78,13 @@ func TestOstreeresolveRef(t *testing.T) {
{srvConf.Srv.URL, "valid/ostree/ref"}: goodRef,
}
for in, expOut := range validCases {
out, err := resolveRef(in.location, in.ref, srvConf.RHSM, srvConf.Subs, &mTLSSrv.CAPath)
out, err := resolveRef(SourceSpec{
in.location,
in.ref,
srvConf.RHSM,
&MTLS{mTLSSrv.CAPath, mTLSSrv.ClientCrtPath, mTLSSrv.ClientKeyPath},
"",
})
assert.NoError(t, err)
assert.Equal(t, expOut, out)
}
Expand All @@ -91,7 +97,13 @@ func TestOstreeresolveRef(t *testing.T) {
{srvConf.Srv.URL, "get_bad_ref"}: fmt.Sprintf("ostree repository \"%s/refs/heads/get_bad_ref\" returned invalid reference", srvConf.Srv.URL),
}
for in, expMsg := range errCases {
_, err := resolveRef(in.location, in.ref, srvConf.RHSM, srvConf.Subs, &mTLSSrv.CAPath)
_, err := resolveRef(SourceSpec{
in.location,
in.ref,
srvConf.RHSM,
&MTLS{mTLSSrv.CAPath, mTLSSrv.ClientCrtPath, mTLSSrv.ClientKeyPath},
"",
})
assert.EqualError(t, err, expMsg)
}
}
Expand Down

0 comments on commit c67d181

Please sign in to comment.