Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use signed-by keyring approach for apt repos in Debian 12+ and Ubuntu 24+ #558

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 56 additions & 8 deletions policies/apt.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ import (
"io"
"net/http"
"sort"
"strconv"
"strings"

"github.com/GoogleCloudPlatform/osconfig/clog"
"github.com/GoogleCloudPlatform/osconfig/osinfo"
"github.com/GoogleCloudPlatform/osconfig/packages"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
Expand Down Expand Up @@ -83,6 +85,52 @@ func containsEntity(es []*openpgp.Entity, e *openpgp.Entity) bool {
return false
}

func readInstanceOsInfo() (string, float64, error) {
oi, err := osinfo.Get()
if err != nil {
return "", 0, fmt.Errorf("error getting osinfo: %v", err)
}

osVersion, err := strconv.ParseFloat(oi.Version, 64)
if err != nil {
osVersion = 0
}

return oi.ShortName, osVersion, nil
}

func shouldUseSignedBy() bool {
osShortName, osVersion, err := readInstanceOsInfo()
if err != nil {
return false // Default to not using signed-by approach
}

if (osShortName == "debian" && osVersion >= 12) || (osShortName == "ubuntu" && osVersion >= 24) {
return true
}
return false
}

func getAptRepoLine(repo *agentendpointpb.AptRepository, useSignedBy bool) string {
archiveType, ok := debArchiveTypeMap[repo.ArchiveType]
if !ok {
archiveType = "deb"
}

line := fmt.Sprintf("\n%s", archiveType)

if useSignedBy {
line = fmt.Sprintf("%s [signed-by=%s]", line, aptGPGFile)
}

line = fmt.Sprintf("%s %s %s", line, repo.Uri, repo.Distribution)
for _, c := range repo.Components {
line = fmt.Sprintf("%s %s", line, c)
}

return line
}

func aptRepositories(ctx context.Context, repos []*agentendpointpb.AptRepository, repoFile string) error {
var es []*openpgp.Entity
var keys []string
Expand Down Expand Up @@ -124,18 +172,18 @@ func aptRepositories(ctx context.Context, repos []*agentendpointpb.AptRepository
# Repo file managed by Google OSConfig agent
deb http://repo1-url/ repo1 main
deb http://repo1-url/ repo2 main contrib non-free

# For now, 'signed-by' keyring approach will be used for Debian 12+ and Ubuntu 24+ only.
To avoid conflicting repos for old stable OSes versions
e.g. deb [signed-by=/etc/apt/trusted.gpg.d/osconfig_agent_managed.gpg] http://repo1-url/ repo1 main
NOTE: suggested by ofca@
*/
var buf bytes.Buffer
buf.WriteString("# Repo file managed by Google OSConfig agent\n")

shouldUseSignedByBool := shouldUseSignedBy()
for _, repo := range repos {
archiveType, ok := debArchiveTypeMap[repo.ArchiveType]
if !ok {
archiveType = "deb"
}
line := fmt.Sprintf("\n%s %s %s", archiveType, repo.Uri, repo.Distribution)
for _, c := range repo.Components {
line = fmt.Sprintf("%s %s", line, c)
}
line := getAptRepoLine(repo, shouldUseSignedByBool)
buf.WriteString(line + "\n")
}

Expand Down
28 changes: 28 additions & 0 deletions policies/apt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,31 @@ func TestGetAptGPGKey(t *testing.T) {
t.Errorf("Expected to find Artifact Registry key in Google Cloud Public GPG key, but its missed.")
}
}

func TestUseSignedBy(t *testing.T) {
tests := []struct {
desc string
repo *agentendpointpb.AptRepository
want string
}{
{
"1 repo",
&agentendpointpb.AptRepository{Uri: "http://repo1-url/", Distribution: "distribution", Components: []string{"component1"}},
"\ndeb [signed-by=/etc/apt/trusted.gpg.d/osconfig_agent_managed.gpg] http://repo1-url/ distribution component1",
},
{
"2 components",
&agentendpointpb.AptRepository{Uri: "http://repo2-url/", Distribution: "distribution", Components: []string{"component1", "component2"}, ArchiveType: agentendpointpb.AptRepository_DEB},
"\ndeb [signed-by=/etc/apt/trusted.gpg.d/osconfig_agent_managed.gpg] http://repo2-url/ distribution component1 component2",
},
}

useSignedBy := true
for _, tt := range tests {
aptRepoLine := getAptRepoLine(tt.repo, useSignedBy)

if aptRepoLine != tt.want {
t.Errorf("%s: got:\n%q\nwant:\n%q", tt.desc, aptRepoLine, tt.want)
}
}
}