Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Please avoid adding duplicate information across this changelog and JIRA/doc inp
## [NEXT RELEASE]

- ROX-8051: The default collection method is changed from KernelModule to eBPF, following improved eBPF performance in collector.
- ROX-11070: There have been changes made to the `v1/groups` API, including a deprecation:
- Each group will now have a new field, `props.id` which uniquely identifies it.
- Get / Update / Mutate / Remove of groups via the `props` field and without the `props.id` field being set is deprecated and will be removed in release 3.73.
- Get of groups via the `props` field and without the `props.id` field being set will fail if more than one group was found for the given `props` field.
- ROX-11349: Updated rationale and remediation texts for default policy "Deployments should have at least one ingress Network Policy"
- ROX-11443: The default value for `--include-snoozed` option of `roxctl image scan` command is set to `false`. The result of `roxctl image scan` execution without `--include-snoozed` flag will not include deferred CVEs anymore.
- ROX-9292: The default expiration time of tokens issued by auth providers has been lowered to 12 hours.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package binenc

import (
"bytes"
"encoding/binary"
"io"

"github.com/pkg/errors"
)

// WriteBytesList takes a list of byte slices and writes them in encoded form to a writer.
func WriteBytesList(w io.Writer, byteSlices ...[]byte) (int, error) {
var total int
for _, byteSlice := range byteSlices {
n, err := WriteUVarInt(w, uint64(len(byteSlice)))
total += n
if err != nil {
return total, err
}

n, err = w.Write(byteSlice)
total += n
if err != nil {
return total, err
}
}
return total, nil
}

// EncodeBytesList takes a list of byte slices and encodes them into a single byte slice.
func EncodeBytesList(byteSlices ...[]byte) []byte {
var buf bytes.Buffer
_, _ = WriteBytesList(&buf, byteSlices...)
return buf.Bytes()
}

// DecodeBytesList takes a byte buffer encoded via EncodeBytesList or WriteBytesList and decodes it into a list of byte
// slices.
func DecodeBytesList(buf []byte) ([][]byte, error) {
var result [][]byte

for len(buf) > 0 {
sliceLen, l := binary.Uvarint(buf)
if l <= 0 {
return nil, errors.New("invalid varint in buffer")
}
buf = buf[l:]
if sliceLen > uint64(len(buf)) {
return nil, errors.Errorf("encountered varint %d which is larger than the remaining buffer size (%d)", sliceLen, len(buf))
}
result = append(result, buf[:sliceLen])
buf = buf[sliceLen:]
}
return result, nil
}
41 changes: 41 additions & 0 deletions migrator/migrations/m_105_to_m_106_group_id/binenc/ints.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package binenc

import "encoding/binary"

// UintEncoder allows encoding uint values directly to byte slices.
type UintEncoder interface {
binary.ByteOrder

EncodeUint16(x uint16) []byte
EncodeUint32(x uint32) []byte
EncodeUint64(x uint64) []byte
}

var (
// BigEndian provides encoding functions for the big endian byte order.
BigEndian = uintEncoder{ByteOrder: binary.BigEndian}
// LittleEndian provides encoding functions for the little endian byte order.
LittleEndian = uintEncoder{ByteOrder: binary.LittleEndian}
)

type uintEncoder struct {
binary.ByteOrder
}

func (e uintEncoder) EncodeUint16(x uint16) []byte {
buf := make([]byte, 2)
e.PutUint16(buf, x)
return buf
}

func (e uintEncoder) EncodeUint32(x uint32) []byte {
buf := make([]byte, 4)
e.PutUint32(buf, x)
return buf
}

func (e uintEncoder) EncodeUint64(x uint64) []byte {
buf := make([]byte, 8)
e.PutUint64(buf, x)
return buf
}
14 changes: 14 additions & 0 deletions migrator/migrations/m_105_to_m_106_group_id/binenc/varint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package binenc

import (
"encoding/binary"
"io"
)

// WriteUVarInt writes the given unsigned integer in its varint representation to the specified writer. The return value
// is the result of calling `Write` with the corresponding byte buffer.
func WriteUVarInt(w io.Writer, x uint64) (int, error) {
var buf [binary.MaxVarintLen64]byte
l := binary.PutUvarint(buf[:], x)
return w.Write(buf[:l])
}
135 changes: 135 additions & 0 deletions migrator/migrations/m_105_to_m_106_group_id/migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package m105tom106

import (
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
"github.com/stackrox/rox/generated/storage"
"github.com/stackrox/rox/migrator/migrations"
"github.com/stackrox/rox/migrator/types"
"github.com/stackrox/rox/pkg/uuid"
bolt "go.etcd.io/bbolt"
)

// groupStoredByCompositeKey is a helper struct which contains the group as well as the composite key.
type groupStoredByCompositeKey struct {
grp *storage.Group
compositeKey []byte
}

var (
bucketName = []byte("groups2")

migration = types.Migration{
StartingSeqNum: 105,
VersionAfter: storage.Version{SeqNum: 106},
Run: func(databases *types.Databases) error {
return migrateGroupsStoredByCompositeKey(databases.BoltDB)
},
}
)

func init() {
migrations.MustRegisterMigration(migration)
}

func migrateGroupsStoredByCompositeKey(db *bolt.DB) error {
groupsWithoutID, err := fetchGroupsToMigrate(db)
if err != nil {
return errors.Wrap(err, "error fetching groups to migrate")
}

if err := addIDsToGroups(db, groupsWithoutID); err != nil {
return errors.Wrap(err, "error adding IDs to group and storing them")
}

if err := removeGroupsStoredByCompositeKey(db, groupsWithoutID); err != nil {
return errors.Wrap(err, "error removing groups without ID")
}

return nil
}

func fetchGroupsToMigrate(db *bolt.DB) (groupsStoredByCompositeKey []groupStoredByCompositeKey, err error) {
err = db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(bucketName)
// Pre-req: Migrating a non-existent bucket should not fail.
if bucket == nil {
return nil
}
return bucket.ForEach(func(k, v []byte) error {
// 1. Try to unmarshal the stored value to the group proto. If it can be successfully unmarshalled, then
// it is stored using the ID as key instead of the serialized key.
if err = proto.Unmarshal(v, &storage.Group{}); err == nil {
return nil
}

// 2. We found a group that is stored using the composite key as index. Deserialize it to a storage.Group.
grp, err := deserialize(k, v)
if err != nil {
return err
}

groupsStoredByCompositeKey = append(groupsStoredByCompositeKey, groupStoredByCompositeKey{grp: grp, compositeKey: k})

return nil
})
})
return groupsStoredByCompositeKey, err
}

func addIDsToGroups(db *bolt.DB, groupsStoredByCompositeKey []groupStoredByCompositeKey) error {
return db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(bucketName)
// Pre-req: Migrating a non-existent bucket should not fail.
if bucket == nil {
return nil
}

for i := range groupsStoredByCompositeKey {
grp := groupsStoredByCompositeKey[i].grp

// 1. Generate the group ID if the group does not already have an ID associated with it.
if grp.GetProps().GetId() == "" {
grp.GetProps().Id = generateGroupID()
}

// 2. Marshal the group proto.
groupData, err := proto.Marshal(grp)
if err != nil {
return err
}

// 3. Save the group using the generated / pre-existing ID as key.
if err := bucket.Put([]byte(grp.GetProps().GetId()), groupData); err != nil {
return err
}
}

return nil
})
}

func removeGroupsStoredByCompositeKey(db *bolt.DB, groupStoredByCompositeKeys []groupStoredByCompositeKey) error {
return db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(bucketName)
// Pre-req: Migrating a non-existent bucket should not fail.
if bucket == nil {
return nil
}

for i := range groupStoredByCompositeKeys {
compositeKey := groupStoredByCompositeKeys[i].compositeKey

// 1. Remove the value stored behind the composite key, since the migrated group is now successfully stored.
if err := bucket.Delete(compositeKey); err != nil {
return err
}
}

return nil
})
}

func generateGroupID() string {
return "io.stackrox.authz.group.migrated." + uuid.NewV4().String()
}
Loading