Compare commits

...

211 Commits
v1.2.1 ... main

Author SHA1 Message Date
Valentin Rothberg 7c6167725b
Merge pull request #193 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.58.0
2025-04-17 09:08:50 +02:00
Valentin Rothberg b63cf81bb5 vendor: use go 1.23
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2025-04-17 08:36:09 +02:00
renovate[bot] e86b33e7c5
fix(deps): update module github.com/containers/storage to v1.58.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 16:11:59 +00:00
Giuseppe Scrivano 71fa86684d
Merge pull request #192 from vrothberg/bump
Bump to v1.2.11
2025-03-31 12:59:03 +02:00
Valentin Rothberg 9d8d33bd67 bump to v1.2.12-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2025-03-31 12:45:30 +02:00
Valentin Rothberg bfdd693beb v1.2.11
Fix an issue where the `read` syscall isn't being correctly recorded on
RHEL 10, see https://issues.redhat.com/browse/RHEL-85351.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2025-03-31 12:44:05 +02:00
Valentin Rothberg 2fe6ea2d9f
Merge pull request #191 from giuseppe/fix-race-record
ebpf: do not lose syscalls happening before the channel is set
2025-03-31 12:41:52 +02:00
Giuseppe Scrivano 4590fdba26
hook: attach tracepoint after the channel is ready
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-03-31 12:01:26 +02:00
Giuseppe Scrivano a028edf9cb
ebpf: fix the code to do what the comment says
a syscall must be ignored before prctl is seen.

Closes: https://issues.redhat.com/browse/RHEL-85351

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-03-31 12:00:32 +02:00
Valentin Rothberg 22a4f30f48
Merge pull request #187 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.1
2025-03-29 14:59:29 +01:00
renovate[bot] 34b21b6dc3
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-29 13:52:46 +00:00
Valentin Rothberg 63fefe0f20
Merge pull request #188 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.62.3
2025-03-29 14:52:04 +01:00
renovate[bot] 591153cb8f
fix(deps): update module github.com/containers/common to v0.62.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 22:20:14 +00:00
Paul Holzinger 8a24390d18
Merge pull request #190 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20250324
2025-03-24 21:23:58 +01:00
renovate[bot] 97f2983630
chore(deps): update dependency containers/automation_images to v20250324
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 19:40:15 +00:00
Valentin Rothberg e2a50af30e
Merge pull request #185 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.62.0
2025-02-04 11:53:29 +01:00
renovate[bot] 1a717caef7
fix(deps): update module github.com/containers/common to v0.62.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 10:45:59 +00:00
Valentin Rothberg d363e25d2a
Merge pull request #186 from vrothberg/lint
update golangci-lint
2025-02-04 11:45:16 +01:00
Valentin Rothberg bdb2268d7b drop Rawhide
It's constantly breaking due to go version mismatches of Rawhide,
golangci and what not.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2025-02-04 11:36:46 +01:00
Valentin Rothberg c5e4904edc
Merge pull request #183 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20250131
2025-02-04 11:17:58 +01:00
renovate[bot] 630a09f34e
chore(deps): update dependency containers/automation_images to v20250131
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 16:51:14 +00:00
Valentin Rothberg 51eaa377ce
Merge pull request #181 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20241107
2024-11-12 09:02:08 +01:00
Valentin Rothberg b8a02cc18f
Merge pull request #180 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.61.0
2024-11-12 09:01:44 +01:00
renovate[bot] 6c8ecb124e
chore(deps): update dependency containers/automation_images to v20241107
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 16:16:24 +00:00
renovate[bot] 6a2d670f51
fix(deps): update module github.com/containers/common to v0.61.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 19:15:31 +00:00
Valentin Rothberg e9fdef4dbf
Merge pull request #178 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.56.0
2024-11-08 10:48:45 +01:00
renovate[bot] 76a55c7ad9
fix(deps): update module github.com/containers/storage to v1.56.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 09:29:07 +00:00
Valentin Rothberg e6400f5b96
Merge pull request #179 from vrothberg/fix-ci
vendor task: use Go 1.22
2024-11-08 10:28:14 +01:00
Valentin Rothberg bad4003415 vendor task: use Go 1.22
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-11-08 09:07:50 +01:00
Valentin Rothberg a26fed5314
Merge pull request #176 from containers/renovate/go-github.com-containers-common-vulnerability
fix(deps): update module github.com/containers/common to v0.60.4 [security]
2024-11-08 09:02:41 +01:00
renovate[bot] 1a9b6396ef
fix(deps): update module github.com/containers/common to v0.60.4 [security]
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-11 10:47:00 +00:00
Valentin Rothberg b03cedfb33
Merge pull request #177 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20241010
2024-10-11 10:40:13 +02:00
renovate[bot] 1326c0606a
chore(deps): update dependency containers/automation_images to v20241010
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-10 22:05:14 +00:00
Valentin Rothberg 898f7b190e
Merge pull request #173 from vrothberg/bump
bump golangci-lint to v1.60.3
2024-08-29 10:59:38 +02:00
Valentin Rothberg 3b4191616c bump golangci-lint to v1.60.3
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-08-29 10:43:43 +02:00
Valentin Rothberg ff7d010baf
Merge pull request #172 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20240821
2024-08-29 10:40:27 +02:00
renovate[bot] 7af78de4f7
chore(deps): update dependency containers/automation_images to v20240821
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-28 20:13:00 +00:00
Valentin Rothberg 00e2d7245d
Merge pull request #171 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.60.2
2024-08-21 09:22:54 +02:00
renovate[bot] f72ed843c0
fix(deps): update module github.com/containers/common to v0.60.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-20 22:08:52 +00:00
Valentin Rothberg 0bc7da7ab1
Merge pull request #169 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.59.2
2024-07-11 09:56:06 +02:00
renovate[bot] 51015d1966
fix(deps): update module github.com/containers/common to v0.59.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 02:05:32 +00:00
Valentin Rothberg f9b9e536f8
Merge pull request #168 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20240529
2024-05-30 10:02:08 +02:00
renovate[bot] 2f5237395e
chore(deps): update dependency containers/automation_images to v20240529
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-29 20:11:26 +00:00
Valentin Rothberg b897a7d464
Merge pull request #165 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.59.0
2024-05-27 08:44:52 +02:00
Valentin Rothberg 7a6c9d3a3d cirrus vendor: use golang 1.21 image
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-05-27 08:28:38 +02:00
Valentin Rothberg 86b2c1c2cc
Merge pull request #166 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20240513
2024-05-27 08:28:06 +02:00
renovate[bot] 266bdfe05f
fix(deps): update module github.com/containers/common to v0.59.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-23 15:07:43 +00:00
renovate[bot] 1ec8ce61a5
chore(deps): update dependency containers/automation_images to v20240513
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-22 21:35:27 +00:00
Daniel J Walsh ba91441159
Merge pull request #167 from cavokz/allow-overriding-the-build-target-dir
Allow overriding the build target dir
2024-05-22 17:34:27 -04:00
Domenico Andreoli 5553f90b1c
Allow overriding the build target dir
In Debian we build the binary in a different location but still we use
`make install` to populate the package content.

Signed-off-by: Domenico Andreoli <domenico.andreoli@linux.com>
2024-05-22 15:34:48 +02:00
Valentin Rothberg 681179f1bd
Merge pull request #163 from containers/renovate/github.com-stretchr-testify-1.x
fix(deps): update module github.com/stretchr/testify to v1.9.0
2024-03-04 09:06:01 +01:00
renovate[bot] b486e8181e
fix(deps): update module github.com/stretchr/testify to v1.9.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-02 14:01:54 +00:00
Valentin Rothberg cd907412d5
Merge pull request #158 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.52.0
2024-02-16 12:56:33 +01:00
renovate[bot] 969537eb7a
fix(deps): update module github.com/containers/storage to v1.52.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-16 09:11:29 +00:00
Valentin Rothberg ec31ff1e86
Merge pull request #162 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.0
2024-02-16 10:10:17 +01:00
renovate[bot] fe7a74ceed
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 16:32:20 +00:00
Valentin Rothberg 01ca720a78
Merge pull request #161 from containers/renovate/go-github.com/opencontainers/runc-vulnerability
chore(deps): update module github.com/opencontainers/runc to v1.1.12 [security]
2024-02-05 12:04:14 +01:00
Valentin Rothberg 57e5d60a3f
Merge pull request #156 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.57.4
2024-02-05 12:03:54 +01:00
renovate[bot] 062cbb9697
fix(deps): update module github.com/containers/common to v0.57.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 10:52:12 +00:00
renovate[bot] 12a6b5a7ef
chore(deps): update module github.com/opencontainers/runc to v1.1.12 [security]
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 10:51:27 +00:00
Valentin Rothberg d7918b036c
Merge pull request #160 from vrothberg/update-ci-image
update CI images
2024-02-05 11:51:00 +01:00
Valentin Rothberg 8b59acfd3d bump golangci-lint to 1.55.2
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-02-05 10:33:54 +01:00
Valentin Rothberg 3082eed8f1
Merge pull request #155 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.57.0
2023-11-17 10:17:21 +01:00
Valentin Rothberg a4c0f838cd
Merge pull request #152 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20231116
2023-11-17 10:16:48 +01:00
renovate[bot] fff84e808d
fix(deps): update module github.com/containers/common to v0.57.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-17 00:05:25 +00:00
renovate[bot] 5ed5002d6c
chore(deps): update dependency containers/automation_images to v20231116
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 21:48:52 +00:00
Valentin Rothberg ad9b137be0
Merge pull request #153 from vrothberg/release
bump to v1.2.10
2023-10-20 10:49:42 +02:00
Valentin Rothberg 1c78fe1b15 bump to v1.2.11-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-10-20 10:36:15 +02:00
Valentin Rothberg a7bafcb864 v1.2.10
* cirrus: vendor: use go 1.19
* vendor c/common@v0.56.0
* bump module to go 1.19
* Update module github.com/containers/common to v0.55.4
* Update module github.com/containers/storage to v1.48.1
* Update module github.com/containers/common to v0.55.3
* Update dependency containers/automation_images to v20230816
* Update dependency containers/automation_images to v20230807
* Update module github.com/opencontainers/runtime-spec to v1.1.0
* Update module github.com/containers/common to v0.55.2
* Update module github.com/containers/common to v0.55.1
* Update module github.com/containers/common to v0.55.0
* Update module github.com/containers/storage to v1.48.0
* Update module github.com/containers/common to v0.54.0
* Update module github.com/containers/storage to v1.47.0
* Update dependency containers/automation_images to v20230614
* Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.3
* Update module github.com/stretchr/testify to v1.8.4
* Update module github.com/sirupsen/logrus to v1.9.3
* Update dependency containers/automation_images to v20230601
* Update module github.com/sirupsen/logrus to v1.9.2
* Update module github.com/stretchr/testify to v1.8.3
* Bump golangci-lint to version 1.51.2
* Cirrus: Enable testing on F38 + Rawhide
* Update module github.com/sirupsen/logrus to v1.9.1
* Update module github.com/containers/common to v0.53.0
* check root privileges
* vendor c/common@v0.52.0
* Update dependency containers/automation_images to v20230405

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-10-20 10:35:17 +02:00
Valentin Rothberg 8cdee7b11e
Merge pull request #150 from vrothberg/vendor
vendor c/common@v0.56.0
2023-09-18 09:55:25 +02:00
Valentin Rothberg e8f359faac cirrus: vendor: use go 1.19
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-09-18 09:09:01 +02:00
Valentin Rothberg c6dd74b3e7 vendor c/common@v0.56.0
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-09-18 09:05:40 +02:00
Valentin Rothberg 86c18727bb bump module to go 1.19
Transitive dependencies already require it.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-09-18 09:05:15 +02:00
Valentin Rothberg 162cf0d113
Merge pull request #148 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.4
2023-08-25 08:49:45 +02:00
renovate[bot] c23a2764ac
Update module github.com/containers/common to v0.55.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-24 17:44:51 +00:00
Valentin Rothberg 3cbd5e812a
Merge pull request #146 from containers/renovate/github.com-containers-storage-1.x
Update module github.com/containers/storage to v1.48.1
2023-08-22 13:18:46 +02:00
renovate[bot] e8062f8abe
Update module github.com/containers/storage to v1.48.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-22 11:06:26 +00:00
Valentin Rothberg a12ae46058
Merge pull request #144 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.3
2023-08-22 13:05:49 +02:00
renovate[bot] a868c45bf8
Update module github.com/containers/common to v0.55.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-22 10:53:35 +00:00
Valentin Rothberg 32c22ee33f
Merge pull request #145 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230816
2023-08-22 12:52:52 +02:00
renovate[bot] 560bae97b8
Update dependency containers/automation_images to v20230816
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-16 23:12:53 +00:00
Valentin Rothberg 8c709c228c
Merge pull request #143 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230807
2023-08-11 13:51:35 +02:00
renovate[bot] d560060775
Update dependency containers/automation_images to v20230807
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-07 20:24:55 +00:00
Valentin Rothberg 21d936bbef
Merge pull request #142 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
Update module github.com/opencontainers/runtime-spec to v1.1.0
2023-07-24 09:13:27 +02:00
renovate[bot] fb98b83d20
Update module github.com/opencontainers/runtime-spec to v1.1.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-22 07:01:39 +00:00
Valentin Rothberg 61c7d317ea
Merge pull request #141 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.2
2023-07-17 08:09:21 +02:00
renovate[bot] 55a5929566
Update module github.com/containers/common to v0.55.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-13 10:28:26 +00:00
Valentin Rothberg e98e224fab
Merge pull request #140 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.1
2023-07-07 08:57:31 +02:00
renovate[bot] 56d0907d7d
Update module github.com/containers/common to v0.55.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-30 15:53:33 +00:00
Valentin Rothberg 9e495e0a80
Merge pull request #139 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.0
2023-06-30 10:15:01 +02:00
renovate[bot] 9745218df4
Update module github.com/containers/common to v0.55.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-30 07:53:58 +00:00
Valentin Rothberg 16395c5ed7
Merge pull request #138 from containers/renovate/github.com-containers-storage-1.x
Update module github.com/containers/storage to v1.48.0
2023-06-30 09:52:46 +02:00
renovate[bot] 8a9af6861b
Update module github.com/containers/storage to v1.48.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-29 19:07:25 +00:00
Valentin Rothberg 8c5ed87d79
Merge pull request #137 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.54.0
2023-06-29 09:14:31 +02:00
renovate[bot] 5d4d04f251
Update module github.com/containers/common to v0.54.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-28 21:55:48 +00:00
Valentin Rothberg ce7d972ba4
Merge pull request #136 from containers/renovate/github.com-containers-storage-1.x
Update module github.com/containers/storage to v1.47.0
2023-06-27 08:16:01 +02:00
renovate[bot] 8c4b4dcc6b
Update module github.com/containers/storage to v1.47.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-27 00:12:04 +00:00
Valentin Rothberg 32e2606d27
Merge pull request #130 from containers/renovate/github.com-stretchr-testify-1.x
Update module github.com/stretchr/testify to v1.8.4
2023-06-15 09:15:16 +02:00
Valentin Rothberg 38f6ab6090
Merge pull request #134 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.3
2023-06-15 09:14:59 +02:00
Valentin Rothberg 5e3793b640
Merge pull request #135 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230614
2023-06-15 09:14:35 +02:00
renovate[bot] 57823f9c42
Update dependency containers/automation_images to v20230614
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-14 15:37:15 +00:00
renovate[bot] 4cc0634fc1
Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-10 10:10:42 +00:00
renovate[bot] 1c8eaf3085
Update module github.com/stretchr/testify to v1.8.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-08 13:28:01 +00:00
Daniel J Walsh d525ee9695
Merge pull request #132 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230601
2023-06-08 07:10:16 -04:00
Daniel J Walsh 37fbd3020f
Merge pull request #133 from containers/renovate/github.com-sirupsen-logrus-1.x
Update module github.com/sirupsen/logrus to v1.9.3
2023-06-08 07:09:47 -04:00
renovate[bot] cf1b3ce101
Update module github.com/sirupsen/logrus to v1.9.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-03 21:19:56 +00:00
renovate[bot] 3b5d827ce8
Update dependency containers/automation_images to v20230601
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-01 18:44:43 +00:00
Valentin Rothberg 967fea3e8e
Merge pull request #127 from containers/renovate/github.com-sirupsen-logrus-1.x
Update module github.com/sirupsen/logrus to v1.9.2
2023-05-22 11:12:59 +02:00
renovate[bot] 39ea9e139f
Update module github.com/sirupsen/logrus to v1.9.2
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-05-22 09:03:40 +00:00
Valentin Rothberg 6fdae39069
Merge pull request #122 from cevich/enable_rawhide_testing
Cirrus: Enable testing on F38 + Rawhide
2023-05-22 11:02:19 +02:00
Valentin Rothberg 28d462ed74
Merge pull request #129 from containers/renovate/github.com-stretchr-testify-1.x
Update module github.com/stretchr/testify to v1.8.3
2023-05-22 11:01:12 +02:00
renovate[bot] 4e983ea172
Update module github.com/stretchr/testify to v1.8.3
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-05-19 03:11:41 +00:00
Chris Evich 89503c8f6b
Bump golangci-lint to version 1.51.2
This is required for compatibility with golang 1.20.  For any version
lower than this, the compiler will panic or fail to build with an error
like: `StringData not declared by package unsafe (typecheck)`

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-05-17 12:21:32 -04:00
Chris Evich ab5d270372
Cirrus: Enable testing on F38 + Rawhide
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-05-17 12:21:31 -04:00
Valentin Rothberg c5eecd3be7
Merge pull request #126 from containers/renovate/github.com-sirupsen-logrus-1.x
Update module github.com/sirupsen/logrus to v1.9.1
2023-05-17 09:44:40 +02:00
renovate[bot] d131bebcca
Update module github.com/sirupsen/logrus to v1.9.1
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-05-16 22:50:44 +00:00
Valentin Rothberg c3a431d834
Merge pull request #124 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.53.0
2023-05-16 11:40:13 +02:00
renovate[bot] 3718eaafa4
Update module github.com/containers/common to v0.53.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-04-27 07:51:54 +00:00
Valentin Rothberg b09d71ad89
Merge pull request #121 from vrothberg/fix-103
check root privileges
2023-04-19 09:45:51 +02:00
Valentin Rothberg 9ca3f9f7b5 check root privileges
Throw an error when the hook isn't executed as root.

Fixes: #103
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-19 09:10:40 +02:00
Valentin Rothberg 3280db1a60
Merge pull request #120 from vrothberg/vendor
vendor c/common@v0.52.0
2023-04-19 08:58:51 +02:00
Valentin Rothberg f1cc6af344
Merge pull request #118 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230405
2023-04-19 08:40:01 +02:00
Valentin Rothberg 00e4b496e1 vendor c/common@v0.52.0
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-19 08:39:10 +02:00
renovate[bot] 28f1b41688
Update dependency containers/automation_images to v20230405
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-04-18 18:52:21 +00:00
Valentin Rothberg 82113f6b3a
Merge pull request #117 from vrothberg/bump
bump to v1.2.9
2023-04-18 17:21:34 +02:00
Valentin Rothberg b9068bfb01 bump back to v1.2.10-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-18 17:14:36 +02:00
Valentin Rothberg 3f6c6065a8 v1.2.9
* Cirrus: Always collect journal after testing
* Cirrus: Update CI VM Images
* fix builds on recent kernels
* Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.2
* Update module github.com/stretchr/testify to v1.8.2
* Use the latest fedora base image
* Add renovate configuration
* bump dependencies
* Updates for golang 1.19
* Cirrus: Update CI VM images to F37
* bump to v1.2.9-dev

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-18 17:13:56 +02:00
Valentin Rothberg 7021239842
Merge pull request #109 from cevich/update_images
Cirrus: Update CI VM Images
2023-04-18 17:12:23 +02:00
Chris Evich bed5713b5a
Cirrus: Always collect journal after testing
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-04-18 10:57:21 -04:00
Chris Evich 19d707a515
Cirrus: Update CI VM Images
Also update env. var. name for consistency with other repos.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-04-18 10:57:21 -04:00
Valentin Rothberg 9ee6f0a8ba
Merge pull request #116 from vrothberg/fix-107
fix builds on recent kernels
2023-04-18 16:51:47 +02:00
Valentin Rothberg bc40b01a56 fix builds on recent kernels
Including bpf.h causes inexplicable farts but I am happy it's working
now.

Fixes: #107
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-18 16:43:05 +02:00
Valentin Rothberg 8cc1f7aedd
Merge pull request #115 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.2
2023-04-18 14:37:32 +02:00
renovate[bot] bd33a9cdce
Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-18 00:27:03 +00:00
Daniel J Walsh e6accd4cfe
Merge pull request #112 from containers/renovate/github.com-stretchr-testify-1.x
Update module github.com/stretchr/testify to v1.8.2
2023-04-14 05:19:25 -04:00
renovate[bot] 544c991c77
Update module github.com/stretchr/testify to v1.8.2
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-04-14 09:08:15 +00:00
Valentin Rothberg 1003459cbc
Merge pull request #111 from containers/renovate/configure
Configure Renovate
2023-04-14 11:07:00 +02:00
Chris Evich 36a96f5d5a
Use the latest fedora base image
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-04-13 15:14:25 -04:00
renovate[bot] 8013aee634
Add renovate configuration
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-04-13 14:57:48 -04:00
Valentin Rothberg a742ba49c8
Merge pull request #110 from vrothberg/deps
bump dependencies
2023-03-17 10:11:56 +01:00
Valentin Rothberg cc4650c778 bump dependencies
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-03-16 12:01:45 +01:00
Valentin Rothberg 872fc48dce
Merge pull request #106 from cevich/F37_ci_vm_images
Cirrus: Update CI VM images to F37
2023-01-19 15:41:09 +01:00
Chris Evich c9476b811f
Updates for golang 1.19
Thanks to @vrothberg for the patch.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-17 11:38:12 -05:00
Chris Evich d6a8b8c7bf
Cirrus: Update CI VM images to F37
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-17 10:34:09 -05:00
Daniel J Walsh b7030ae105
Merge pull request #105 from vrothberg/version
bump to v1.2.8
2022-10-17 09:16:33 -04:00
Valentin Rothberg 2258f0a2ce bump to v1.2.9-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-10-12 09:57:39 +02:00
Valentin Rothberg ee91033618 v1.2.8
* update gobpf: fix compatibility with bcc v0.25.0

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-10-12 09:57:26 +02:00
Valentin Rothberg 2543f4f6e5
Merge pull request #104 from martinetd/gobpf
update gobpf: fix compatibility with bcc v0.25.0
2022-10-12 09:45:35 +02:00
Dominique Martinet 245cc45ad4 update gobpf: fix compatibility with bcc v0.25.0
bcc v0.25.0 broke compatibility with bcc_func_load and requires updating
the gobpf wrapper.

Fixes: #100
Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
2022-10-11 22:40:42 +09:00
Giuseppe Scrivano 934aee1fc5
Merge pull request #102 from vrothberg/version
bump to v1.2.8
2022-09-01 16:54:03 +02:00
Valentin Rothberg 3fee319eaf bump to v1.2.8-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-09-01 10:46:48 +02:00
Valentin Rothberg e33c59f550 v1.2.7
* README: add copyright notice
* Cirrus: Use the latest imgts container

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-09-01 10:46:33 +02:00
Daniel J Walsh ad1d706bc1
Merge pull request #101 from vrothberg/fix-99
README: add copyright notice
2022-08-31 09:25:01 -04:00
Valentin Rothberg 0dd7377070 README: add copyright notice
Add a copyright notice to the README.

Fixes: #99
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-08-31 13:29:01 +02:00
Daniel J Walsh 1ac29b4441
Merge pull request #98 from cevich/latest_imgts
Cirrus: Use the latest imgts container
2022-07-27 11:13:20 -04:00
Chris Evich 4de230431d
Cirrus: Use the latest imgts container
Contains important updates re: preserving release-branch CI VM images.
Ref: https://github.com/containers/automation_images/pull/157

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-07-26 14:43:20 -04:00
Daniel J Walsh e3e0f2acff
Merge pull request #97 from vrothberg/ci-test
Make CI healthy
2022-07-11 08:42:41 -04:00
Valentin Rothberg 623923ef48 bump to v1.2.7-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-07-11 14:05:43 +02:00
Valentin Rothberg bdb5040dc4 v1.2.6
* use containers/common/pkg/seccomp
* fix `make validate`
* CI: squash build & test tasks
* CI: use newer libpod image
* CI: bump golangci-lint to v1.45.2
* CI: bump to golang:1.18 image

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-07-11 14:05:01 +02:00
Valentin Rothberg afc6afabe4 use containers/common/pkg/seccomp
Replaces the deprecated seccomp/containers-golang package.

Fixes: #96
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-07-11 14:03:49 +02:00
Valentin Rothberg c57f0ab6d2 fix `make validate`
The path was wrong.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-07-11 13:47:15 +02:00
Valentin Rothberg 5c855cb55a CI: squash build & test tasks
Having two VMs essentially doing 80 percent of the same tasks is not a
good use of resources and testing time.  Squash the two tasks together.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-07-11 13:43:49 +02:00
Valentin Rothberg 1244014eee CI: use newer libpod image
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-07-11 13:28:42 +02:00
Valentin Rothberg ef71fe9e52 CI: bump golangci-lint to v1.45.2
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-07-11 13:25:40 +02:00
Valentin Rothberg 998f008301 CI: bump to golang:1.18 image
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-07-11 13:21:25 +02:00
Valentin Rothberg 5d1ff36303
Merge pull request #95 from vrothberg/update-dependencies
Update dependencies
2022-03-16 09:45:37 +01:00
Valentin Rothberg 7327b3c07e bump to v1.2.6-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-03-14 14:26:44 +01:00
Valentin Rothberg a5abc427ed v1.2.5
* fix CVE-2019-11254: require gopkg.in/yaml.v2@v2.2.8

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-03-14 14:26:38 +01:00
Valentin Rothberg 7513108440 fix CVE-2019-11254: require gopkg.in/yaml.v2@v2.2.8
Just popped up in the Dependabot alerts.

Closes: github.com/containers/oci-seccomp-bpf-hook/security/dependabot/1
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-03-14 14:26:32 +01:00
Valentin Rothberg 7f40ff45a2
Merge pull request #94 from vrothberg/update-dependencies
Update dependencies
2022-03-14 14:20:53 +01:00
Valentin Rothberg 81d698b67b bump to v1.2.5-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-03-14 13:57:20 +01:00
Valentin Rothberg 8b16fc7912 v1.2.4
* migrate to native error handling
* update dependencies
* Remove unused `zero_64` variable
* Do not record empty syscall names
* [CI:DOCS] Update link master -> main
* Fix docs links due to branch rename
* bump github.com/iovisor/gobpf v0.1.1 -> v0.2.0
* Update F34beta -> F34

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-03-14 13:57:20 +01:00
Valentin Rothberg f49c1ba592 migrate to native error handling
Drop `pkg/errors` in favor of using native error handling.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-03-14 13:57:17 +01:00
Valentin Rothberg aac3eb4fa5 update dependencies
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-03-14 13:23:48 +01:00
Daniel J Walsh c378484c29
Merge pull request #92 from saschagrunert/zero-u64
Remove unused `zero_64` variable
2021-10-12 13:54:33 -04:00
Sascha Grunert 65ccce260b
Remove unused `zero_64` variable
The variable seems unused and therefore can be removed.

Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
2021-10-12 19:35:57 +02:00
Daniel J Walsh 6385f4e518
Merge pull request #90 from cevich/update_images
Cirrus: Freshen VM images
2021-09-14 13:51:25 -04:00
Chris Evich 641cabfc84
Cirrus: Freshen VM images
Signed-off-by: Chris Evich <cevich@redhat.com>
2021-09-14 12:57:35 -04:00
Daniel J Walsh 7a25813f29
Merge pull request #89 from saschagrunert/syscall-name
Do not record empty syscall names
2021-07-16 06:48:44 -04:00
Sascha Grunert bd2d0c048e
Do not record empty syscall names
If a syscall name cannot be retrieved, then we should not add it to the
map of found syscalls to omit writing `""` to the profile.

Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
2021-07-16 12:00:39 +02:00
Valentin Rothberg 9fac5591f3
Merge pull request #88 from cevich/master-to-main
[CI:DOCS] Update link master -> main
2021-06-21 09:49:26 +02:00
Chris Evich ca92d4d70e
[CI:DOCS] Update link master -> main
Signed-off-by: Chris Evich <cevich@redhat.com>
2021-06-18 14:21:24 -04:00
Valentin Rothberg 4f6665442d
Merge pull request #87 from cevich/fix_links
[CI:DOCS] Fix docs links due to branch rename
2021-06-10 17:29:35 +02:00
Chris Evich 76f6883c86
Fix docs links due to branch rename
Ref: https://github.com/containers/common/issues/549

Signed-off-by: Chris Evich <cevich@redhat.com>
2021-06-10 11:25:35 -04:00
Giuseppe Scrivano 4a30d950c6
Merge pull request #85 from lsm5/bump-gobpf-0.2.0
bump github.com/iovisor/gobpf v0.1.1 -> v0.2.0
2021-05-06 14:33:57 +02:00
Lokesh Mandvekar e568b43963 bump github.com/iovisor/gobpf v0.1.1 -> v0.2.0
This fixes build issues on 32-bit arches

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2021-05-06 06:40:40 -04:00
Valentin Rothberg 511749e593
Merge pull request #83 from cevich/update_f34
Update F34beta -> F34
2021-04-30 08:16:49 +02:00
Chris Evich 70c739725d
Update F34beta -> F34
Also update library to match similar changes in other containers-org.
repos.

Signed-off-by: Chris Evich <cevich@redhat.com>
2021-04-29 13:03:15 -04:00
Daniel J Walsh 1910bb0fdb
Merge pull request #82 from vrothberg/bump
Bump to v1.2.3
2021-04-27 06:18:45 -04:00
Valentin Rothberg 70364b44d1 bump to v1.2.4-dev
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-04-27 09:26:23 +02:00
Valentin Rothberg b8769db559 v1.2.3
bump github.com/iovisor/gobpf to v0.1.1 to fix a build issue on armv7hl

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-04-27 09:25:52 +02:00
Valentin Rothberg feff03e6dc
Merge pull request #81 from lsm5/vendor-iovisor-gobpf-0.1.1
bump github.com/iovisor/gobpf to v0.1.1
2021-04-26 16:50:18 +02:00
Lokesh Mandvekar f5cea64ac7 bump github.com/iovisor/gobpf to v0.1.1
This fixes a build issue on armv7hl

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2021-04-26 10:32:29 -04:00
Daniel J Walsh b059fab415
Merge pull request #80 from vrothberg/bump
bump to v1.2.2
2021-04-20 10:20:32 -04:00
Valentin Rothberg a076a6ff65 bump to v1.2.3-dev
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-04-20 10:55:05 +02:00
Valentin Rothberg c16213717f v1.2.2
* cirrus: set selinux to permissive
* hook: delay SIGUSR1
* Cirrus: Use shared get_ci_vm container image
* Cirrus: Update to F34beta VM image
* ebpf: check for NULL
* ebpf: move prctl handling to ebpf
* ebpf: memorize what syscalls were notified
* Test suite: minimal tweaks for gating environment

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-04-20 10:54:21 +02:00
Valentin Rothberg 8a276e7fe6
Merge pull request #78 from giuseppe/handle-prctl-ebpf
ebpf: move prctl handling to ebpf
2021-04-16 08:31:35 +02:00
Daniel J Walsh feb3681126
Merge pull request #79 from giuseppe/switch-to-f34beta
Update VM image to F34beta + use shared hack/get_ci_vm.sh
2021-04-15 19:37:59 -04:00
Giuseppe Scrivano 32107099d0
cirrus: set selinux to permissive
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2021-04-15 23:20:42 +02:00
Giuseppe Scrivano a5a1db612a
hook: delay SIGUSR1
fixes a race condition where the parent is notified too quickly.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2021-04-15 23:20:30 +02:00
Chris Evich 89e1fbb867
Cirrus: Use shared get_ci_vm container image
Signed-off-by: Chris Evich <cevich@redhat.com>
2021-04-15 23:20:29 +02:00
Chris Evich 6eb2ec5d88
Cirrus: Update to F34beta VM image
The new image includes a library of common automation functions.  Make
use of this to prevent divergence of duplicated of code.  Remove
duplicated code.

Signed-off-by: Chris Evich <cevich@redhat.com>
2021-04-15 23:20:28 +02:00
Giuseppe Scrivano 53f03f5398
ebpf: check for NULL
lookup_or_init can return NULL

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2021-04-15 21:57:11 +02:00
Giuseppe Scrivano ba736858dd
ebpf: move prctl handling to ebpf
move all the prctl handling to the same place.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2021-04-15 21:52:48 +02:00
Daniel J Walsh bddafc93a9
Merge pull request #77 from giuseppe/memorize-syscalls
ebpf: memorize what syscalls were notified
2021-04-15 12:58:41 -04:00
Giuseppe Scrivano 9fcc943fd4
ebpf: memorize what syscalls were notified
avoid generating too many perf events by memorizing what syscalls were
already seen and notified.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2021-04-15 16:33:36 +02:00
Valentin Rothberg 50e7112d7d
Merge pull request #73 from edsantiago/gating_tests
Test suite: minimal tweaks for gating environment
2021-01-28 09:36:24 +01:00
Ed Santiago ab48870ff9 Test suite: minimal tweaks for gating environment
In preparation for creating a -tests subpackage for use in
Fedora gating tests:

 * Use alpine image from quay.io, to avoid docker.io throttling
 * Find block-mkdir.json relative to helpers.bash file, not source dir
 * Skip the version test, it can't work in an rpm environment
 * Use github.com as a ping destination host (google.com is
   unreachable from one of our CI environments).

Because my editor demands it and it's proper practice:

 * Clean up trailing whitespace

Signed-off-by: Ed Santiago <santiago@redhat.com>
2021-01-27 14:48:28 -07:00
Valentin Rothberg 4e42394b75
Merge pull request #71 from vrothberg/v1.2.1
v1.2.1
2021-01-27 13:29:50 +01:00
Valentin Rothberg 4a24d7e884 bump to v1.2.2-dev
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-01-27 13:16:31 +01:00
Valentin Rothberg 040dd03b90
Merge pull request #68 from vrothberg/new_images
Update Cirrus Images + follow-up fixes
2021-01-27 13:03:54 +01:00
628 changed files with 94523 additions and 30012 deletions

View File

@ -11,8 +11,8 @@ env:
GOCACHE: "${HOME}/.cache/go-build"
# VM Image built in containers/automation_images
_BUILT_IMAGE_SUFFIX: "c6524344056676352"
FEDORA_CACHE_IMAGE_NAME: "fedora-${_BUILT_IMAGE_SUFFIX}"
IMAGE_SUFFIX: "c20250324t111922z-f41f40d13"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
# Must be defined true when testing w/in containers
CONTAINER: "false"
@ -21,21 +21,11 @@ env:
gcp_credentials: ENCRYPTED[2ba9cffb563741f8538eab6d4a8b2d4684c0de23693a8ade80aced34596669a87a6c01e45ce45fad7f7db1d995e8c777]
# Default VM to use unless set or modified by task
gce_instance:
image_project: "${IMAGE_PROJECT}"
zone: "us-central1-c" # Required by Cirrus for the time being
cpu: 2
memory: "4Gb"
disk: 200 # Required for performance reasons
image_name: "${FEDORA_CACHE_IMAGE_NAME}"
# Update metadata on VM images referenced by this repository state
meta_task:
container:
image: "quay.io/libpod/imgts:master" # maintained in libpod repo
image: "quay.io/libpod/imgts:latest"
cpu: 1
memory: 1
@ -58,49 +48,27 @@ meta_task:
vendor_task:
container:
image: golang:1.14
image: golang:1.23
script:
- make vendor
# build binary and docs
build_task:
# Uses VM via default gce_instance (above)
gce_instance:
matrix:
image_name: $FEDORA_CACHE_IMAGE_NAME
# Avoid downloading this stuff every time
gocache_cache:
folder: "${GOCACHE}"
fingerprint_script: $SCRIPT_BASE/cache_fingerprint.sh
# Avoid needless rebuilding of tooling binaries
gopath_cache:
folder: "${GOPATH}/bin"
fingerprint_script: $SCRIPT_BASE/cache_fingerprint.sh
# Avoid needless rebuilding of source binaries
gosrc_bin_cache:
folder: "${CIRRUS_WORKING_DIR}/bin"
fingerprint_script: $SCRIPT_BASE/cache_fingerprint.sh
script:
- $SCRIPT_BASE/setup.sh
- make binary
- make install.tools
- make validate
- make docs
# run unit and integration tests
test_task:
build_and_test_task:
gce_instance:
matrix:
image_name: $FEDORA_CACHE_IMAGE_NAME
image_project: "${IMAGE_PROJECT}"
zone: "us-central1-c" # Required by Cirrus for the time being
cpu: 2
memory: "4Gb"
disk: 200 # Required for performance reasons
image_name: "${FEDORA_CACHE_IMAGE_NAME}"
matrix:
- name: "Test on Fedora"
gce_instance:
image_name: "${FEDORA_CACHE_IMAGE_NAME}"
# Avoid downloading this stuff every time
gocache_cache:
@ -125,10 +93,17 @@ test_task:
script:
- $SCRIPT_BASE/setup.sh
- make binary
- make install.tools
- make validate
- make docs
- sudo make PREFIX=/usr install
- make test-unit
- sudo make test-integration
# The hook runs in the background, errors will be in the journal.
always:
journal_script: journalctl -b
binaries_artifacts:
path: "bin/*"
type: "application/octet-stream"

54
.github/renovate.json5 vendored Normal file
View File

@ -0,0 +1,54 @@
/*
Renovate is a service similar to GitHub Dependabot, but with
(fantastically) more configuration options. So many options
in fact, if you're new I recommend glossing over this cheat-sheet
prior to the official documentation:
https://www.augmentedmind.de/2021/07/25/renovate-bot-cheat-sheet
Configuration Update/Change Procedure:
1. Make changes
2. Manually validate changes (from repo-root):
podman run -it \
-v ./.github/renovate.json5:/usr/src/app/renovate.json5:z \
docker.io/renovate/renovate:latest \
renovate-config-validator
3. Commit.
Configuration Reference:
https://docs.renovatebot.com/configuration-options/
Monitoring Dashboard:
https://app.renovatebot.com/dashboard#github/containers
Note: The Renovate bot will create/manage it's business on
branches named 'renovate/*'. Otherwise, and by
default, the only the copy of this file that matters
is the one on the `main` branch. No other branches
will be monitored or touched in any way.
*/
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
/*************************************************
****** Global/general configuration options *****
*************************************************/
// Re-use predefined sets of configuration options to DRY
"extends": [
// https://github.com/containers/automation/blob/main/renovate/defaults.json5
"github>containers/automation//renovate/defaults.json5"
],
// Permit automatic rebasing when base-branch changes by more than
// one commit.
"rebaseWhen": "behind-base-branch",
/*************************************************
*** Repository-specific configuration options ***
*************************************************/
"assignees": ["vrothberg", "rhatdan"],
}

View File

@ -1,3 +1,3 @@
## The oci-seccomp-bpf-hook Project Community Code of Conduct
The oci-seccomp-bpf-hook project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md).
The oci-seccomp-bpf-hook project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/main/CODE-OF-CONDUCT.md).

View File

@ -6,6 +6,7 @@ GO_BUILD=$(GO) build
ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true)
GO_BUILD=GO111MODULE=on $(GO) build -mod=vendor
endif
BUILDDIR ?= .
DESTDIR ?=
PREFIX ?= /usr/local
SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z)
@ -46,11 +47,11 @@ docs:
.PHONY: binary
binary:
$(GO_BUILD) -mod=vendor -o bin/oci-seccomp-bpf-hook -ldflags "-X main.version=$(OSBH_VERSION)" $(PROJECT)
$(GO_BUILD) -mod=vendor -o $(BUILDDIR)/bin/oci-seccomp-bpf-hook -ldflags "-X main.version=$(OSBH_VERSION)" $(PROJECT)
.PHONY: validate
validate:
$(GOBIN)/golangci-lint run
./build/golangci-lint run
.PHONY: vendor
vendor:
@ -73,10 +74,9 @@ test-unit:
.PHONY: install.tools
install.tools: .install.golangci-lint .install.md2man
.install.golangci-lint: VERSION=v1.60.3
.install.golangci-lint:
if [ ! -x "$(GOBIN)/golangci-lint" ]; then \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOBIN)/ v1.18.0; \
fi
curl -fsSL https://raw.githubusercontent.com/golangci/golangci-lint/$(VERSION)/install.sh | sh -s -- -b ./build $(VERSION)
.install.md2man:
if [ -z "$(shell type -P go-md2man)" ]; then \
@ -95,7 +95,7 @@ install.docs:
install-nobuild: install.docs-nobuild
install $(SELINUXOPT) -d -m 755 $(DESTDIR)$(HOOK_BIN_DIR)
install $(SELINUXOPT) -d -m 755 $(DESTDIR)$(HOOK_DIR)
install $(SELINUXOPT) -m 755 bin/oci-seccomp-bpf-hook $(DESTDIR)$(HOOK_BIN_DIR)
install $(SELINUXOPT) -m 755 $(BUILDDIR)/bin/oci-seccomp-bpf-hook $(DESTDIR)$(HOOK_BIN_DIR)
install $(SELINUXOPT) -m 644 oci-seccomp-bpf-hook.json $(DESTDIR)$(HOOK_DIR)
sed -i 's|HOOK_BIN_DIR|$(HOOK_BIN_DIR)|g' $(DESTDIR)$(HOOK_DIR)/oci-seccomp-bpf-hook.json

View File

@ -1,4 +1,4 @@
[![Build Status](https://api.cirrus-ci.com/github/containers/oci-seccomp-bpf-hook.svg)](https://cirrus-ci.com/github/containers/oci-seccomp-bpf-hook/master)
[![Build Status](https://api.cirrus-ci.com/github/containers/oci-seccomp-bpf-hook.svg)](https://cirrus-ci.com/github/containers/oci-seccomp-bpf-hook/main)
# oci-seccomp-bpf-hook
@ -23,3 +23,5 @@ sudo podman run --annotation io.containers.trace-syscall="if:[absolute path to t
The profile will be created at the output path provided to the annotation. Providing `of:` is mandatory, while `if:` is optional. An input file can be used to create a baseline and newly recorded syscalls will be added to the set and written to the output. If a syscall is blocked in the base profile, then it will remain blocked in the output file even if it is recorded while tracing.
Please refer to an article on [Enable Sysadmin](https://www.redhat.com/sysadmin/container-security-seccomp) for more details.
`Copyright {2018-2022} {containers/oci-seccomp-bpf-hook maintainers}`

View File

@ -1,3 +1,3 @@
## Security and Disclosure Information Policy for the oci-seccomp-bpf-hook Project
The oci-seccomp-bpf-hook Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/master/SECURITY.md) for the Containers Projects.
The oci-seccomp-bpf-hook Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/main/SECURITY.md) for the Containers Projects.

View File

@ -1 +1 @@
v1.2.1
v1.2.12-dev

View File

@ -1,4 +1,4 @@
FROM fedora:31
FROM fedora:latest
ENV GOPATH=/var/tmp/go
ENV GOSRC=$GOPATH/src/github.com/containers/oci-seccomp-bpf-hook

View File

@ -1,61 +1,50 @@
#!/bin/bash
set -e
# Library of common, shared utility functions. This file is intended
# to be sourced by other scripts, not called directly.
# BEGIN Global export of all variables
set -a
# Due to differences across platforms and runtime execution environments,
# handling of the (otherwise) default shell setup is non-uniform. Rather
# than attempt to workaround differences, simply force-load/set required
# items every time this library is utilized.
USER="$(whoami)"
HOME="$(getent passwd $USER | cut -d : -f 6)"
# Some platforms set and make this read-only
[[ -n "$UID" ]] || \
UID=$(getent passwd $USER | cut -d : -f 3)
# Automation library installed at image-build time,
# defining $AUTOMATION_LIB_PATH in this file.
if [[ -r "/etc/automation_environment" ]]; then
source /etc/automation_environment
fi
# shellcheck disable=SC2154
if [[ -n "$AUTOMATION_LIB_PATH" ]]; then
# shellcheck source=/usr/share/automation/lib/common_lib.sh
source $AUTOMATION_LIB_PATH/common_lib.sh
else
(
echo "WARNING: It does not appear that containers/automation was installed."
echo " Functionality of most of this library will be negatively impacted"
echo " This ${BASH_SOURCE[0]} was loaded by ${BASH_SOURCE[1]}"
) > /dev/stderr
fi
GOSRC="${GOSRC:-$(realpath $(dirname $0)/../../)}"
SCRIPT_BASE="${SCRIPT_BASE:-./contrib/cirrus}"
# GCE image-name compatible string representation of distribution name
OS_RELEASE_ID="$(source /etc/os-release; echo $ID)"
# GCE image-name compatible string representation of distribution _major_ version
OS_RELEASE_VER="$(source /etc/os-release; echo $VERSION_ID | cut -d '.' -f 1)"
# Combined to ease soe usage
OS_REL_VER="${OS_RELEASE_ID}-${OS_RELEASE_VER}"
# Set 'true' when operating inside a container
CONTAINER="${CONTAINER:-false}"
if type -P go &> /dev/null; then
set -a && eval "$(go env)" && set +a
eval "$(go env)"
fi
die() {
echo "************************************************"
echo ">>>>> ${1:-FATAL ERROR (but no message given!) in ${FUNCNAME[1]}()}"
echo "************************************************"
exit 1
}
# Pass in a list of one or more envariable names; exit non-zero with
# helpful error message if any value is empty
req_env_var() {
# Provide context. If invoked from function use its name; else script name
local caller=${FUNCNAME[1]}
if [[ -n "$caller" ]]; then
# Indicate that it's a function name
caller="$caller()"
else
# Not called from a function: use script name
caller=$(basename $0)
fi
# Usage check
[[ -n "$1" ]] || die "FATAL: req_env_var: invoked without arguments"
# Each input arg is an envariable name, e.g. HOME PATH etc. Expand each.
# If any is empty, bail out and explain why.
for i; do
if [[ -z "${!i}" ]]; then
die "FATAL: $caller requires \$$i to be non-empty"
fi
done
}
# Helper/wrapper script to only show stderr/stdout on non-zero exit
install_ooe() {
req_env_var GOSRC SCRIPT_BASE
echo "Installing script to mask stdout/stderr unless non-zero exit."
sudo install -D -m 755 "$GOSRC/$SCRIPT_BASE/ooe.sh" /usr/local/bin/ooe.sh
}
# END Global export of all variables
set +a
bad_os_id_ver() {
echo "Unknown/Unsupported distro. $OS_RELEASE_ID and/or version $OS_RELEASE_VER for $(basename $0)"

View File

@ -1,39 +0,0 @@
#!/bin/bash
# This script executes a command while logging all output to a temporary
# file. If the command exits non-zero, then all output is sent to the console,
# before returning the exit code. If the script itself fails, the exit code 121
# is returned.
set -eo pipefail
SCRIPT_BASEDIR="$(basename $0)"
badusage() {
echo "Incorrect usage: $SCRIPT_BASEDIR) <command> [options]" > /dev/stderr
echo "ERROR: $1"
exit 121
}
COMMAND="$@"
[[ -n "$COMMAND" ]] || badusage "No command specified"
OUTPUT_TMPFILE="$(mktemp -p '' ${SCRIPT_BASEDIR}_output_XXXX)"
output_on_error() {
RET=$?
set +e
if [[ "$RET" -ne "0" ]]
then
echo "---------------------------"
cat "$OUTPUT_TMPFILE"
echo "[$(date --iso-8601=second)] <exit $RET> $COMMAND"
fi
rm -f "$OUTPUT_TMPFILE"
}
trap "output_on_error" EXIT
"$@" 2>&1 | while IFS='' read LINE # Preserve leading/trailing whitespace
do
# Every stdout and (copied) stderr line
echo "[$(date --iso-8601=second)] $LINE"
done >> "$OUTPUT_TMPFILE"

View File

@ -6,10 +6,9 @@ set -e
source $(dirname $0)/lib.sh
cd $GOSRC
req_env_vars OS_RELEASE_ID GOSRC
# Only Output on Error wrapper
install_ooe
cd $GOSRC
CRITICAL_PKGS=()
INSTALL_PACKAGES=()
@ -93,7 +92,7 @@ case "$OS_RELEASE_ID" in
python
python3-dateutil
python3-psutil
python3-pytoml
python3-toml
selinux-policy-devel
unzip
vim
@ -101,14 +100,6 @@ case "$OS_RELEASE_ID" in
xz
zip
)
# Some small differences between 30 and 31
case "$OS_RELEASE_VER" in
33)
INSTALL_PACKAGES+=(crun)
;;
*)
bad_os_id_ver ;;
esac
;;
*)
bad_os_id_ver
@ -120,12 +111,6 @@ if [[ "${#INSTALL_PACKAGES[@]}" -gt "0" ]]; then
ooe.sh $INSTALL_COMMAND ${INSTALL_PACKAGES[@]}
fi
if [[ "$OS_RELEASE_ID" == "fedora" ]] && [[ -r "/usr/libexec/podman/conmon" ]]
then
echo "Warning: Working around podman 1.5 w/ embedded conmon."
rm -vf '/usr/libexec/podman/conmon'
fi
# Some variables change after package install
source $(dirname $0)/lib.sh
@ -135,3 +120,7 @@ ooe.sh make install.tools
echo "Names and versions of critical packages"
NOT_INSTALLED_RE='(package .+ is not installed)|(no packages found matching .+)'
$LIST_COMMAND ${CRITICAL_PKGS[@]} | sed -r -e "s/$NOT_INSTALLED_RE/ > > \0/" | sort
show_env_vars
setenforce 0

View File

@ -4,14 +4,16 @@ set -e
source $(dirname $0)/lib.sh
req_env_vars GOSRC
BIN=bin/oci-seccomp-bpf-hook
cd $GOSRC
# This should have been populated by cache, but if not just build again
if [[ ! -x "$BIN" ]]; then
echo "Warning: $BIN not found, expecting to find it cached as 'gosrc' from build_task"
echo "Re-building binaries"
warn "$BIN not found, expecting to find it cached as 'gosrc' from build_task"
msg "Re-building binaries"
make
fi
@ -20,8 +22,8 @@ fi
ls -la bin
echo "Installing oci-seccomp-bpf-hook"
msg "Installing oci-seccomp-bpf-hook"
make install PREFIX=/usr
echo "Executing integration tests"
msg "Executing integration tests"
make test-integration BATS_OPTS=--tap

View File

@ -2,6 +2,10 @@
set -e
set -a && eval "$(go env)" && set +a
source $(dirname $0)/lib.sh
req_env_vars GOSRC
cd $GOSRC
make validate

View File

@ -4,6 +4,8 @@ set -e
source $(dirname $0)/lib.sh
req_env_vars GOSRC
cd $GOSRC
make vendor

42
ebpf.go
View File

@ -18,7 +18,6 @@ type event struct {
// Complete documentation is available at
// https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md
const source string = `
#include <linux/bpf.h>
#include <linux/nsproxy.h>
#include <linux/pid_namespace.h>
#include <linux/ns_common.h>
@ -38,6 +37,8 @@ struct mnt_namespace {
// of the processes inside the container.
BPF_HASH(parent_namespace, u64, unsigned int);
BPF_HASH(seen_syscalls, int, u64);
// Opens a custom BPF table to push data to user space via perf ring buffer
BPF_PERF_OUTPUT(events);
@ -68,9 +69,10 @@ int enter_trace(struct tracepoint__raw_syscalls__sys_enter* args)
struct task_struct *task;
struct nsproxy *nsproxy;
struct mnt_namespace *mnt_ns;
int id = (int)args->id;
data.pid = bpf_get_current_pid_tgid();
data.id = (int)args->id;
data.id = id;
bpf_get_current_comm(&data.comm, sizeof(data.comm));
task = (struct task_struct *)bpf_get_current_task();
@ -84,12 +86,46 @@ int enter_trace(struct tracepoint__raw_syscalls__sys_enter* args)
}
unsigned int* parent_inum = parent_namespace.lookup_or_init(&key, &zero);
if (*parent_inum != inum) {
if (parent_inum != NULL && *parent_inum != inum) {
return 0;
}
u64 seen = 0, *tmp = seen_syscalls.lookup(&id);
if (tmp != NULL)
seen = *tmp;
// Syscalls are not recorded until prctl() is called. The first
// invocation of prctl is guaranteed to happen by the supported
// OCI runtimes (i.e., runc and crun) as it's being called when
// setting the seccomp profile.
if (id == __NR_prctl) {
// The syscall was already notified.
if (seen > 1)
return 0;
// The first time we see prctl, we record it without generating
// any event.
if (seen == 0) {
goto record_and_exit;
}
} else {
// The syscall was already notified.
if (seen > 0)
return 0;
// if prctl was not seen, ignore the current syscall.
u64 prctl = __NR_prctl;
u64 *prctl_seen = seen_syscalls.lookup(&prctl);
if (prctl_seen == NULL) {
return 0;
}
}
data.stopTracing = false;
events.perf_submit(args, &data, sizeof(data));
record_and_exit:
seen++;
seen_syscalls.update(&id, &seen);
return 0;
}

32
go.mod
View File

@ -1,14 +1,28 @@
module github.com/containers/oci-seccomp-bpf-hook
go 1.14
go 1.23.0
toolchain go1.23.8
require (
github.com/iovisor/gobpf v0.0.0-20210109143822-fb892541d416
github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
github.com/pkg/errors v0.9.1
github.com/seccomp/containers-golang v0.6.0
github.com/seccomp/libseccomp-golang v0.9.1
github.com/sirupsen/logrus v1.7.0
github.com/stretchr/testify v1.4.0
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect
github.com/containers/common v0.62.3
github.com/containers/storage v1.58.0
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4
github.com/opencontainers/runtime-spec v1.2.1
github.com/seccomp/libseccomp-golang v0.10.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
)
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
golang.org/x/sys v0.32.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

105
go.sum
View File

@ -1,60 +1,55 @@
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/containers/common v0.62.3 h1:aOGryqXfW6aKBbHbqOveH7zB+ihavUN03X/2pUSvWFI=
github.com/containers/common v0.62.3/go.mod h1:3R8kDox2prC9uj/a2hmXj/YjZz5sBEUNrcDiw51S0Lo=
github.com/containers/storage v1.58.0 h1:Q7SyyCCjqgT3wYNgRNIL8o/wUS92heIj2/cc8Sewvcc=
github.com/containers/storage v1.58.0/go.mod h1:w7Jl6oG+OpeLGLzlLyOZPkmUso40kjpzgrHUk5tyBlo=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/iovisor/gobpf v0.0.0-20210109143822-fb892541d416 h1:KIP9lHDm4Y+rNr6ONL+5fC5lX+79FgRlF6Xmi9VasVk=
github.com/iovisor/gobpf v0.0.0-20210109143822-fb892541d416/go.mod h1:+5U5qu5UOu8YJ5oHVLvWKH7/Dr5QNHU7mZ2RfPEeXg8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/opencontainers/runtime-spec v1.0.3-0.20200710190001-3e4195d92445/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6 h1:NhsM2gc769rVWDqJvapK37r+7+CBXI8xHhnfnt8uQsg=
github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU=
github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.6.0 h1:+bIAS/Za3q5FTwWym4fTB0vObnfCf3G/NC7K6Jx62mY=
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4 h1:WpizD4VUT5V+VcaQSvW5BlvFpQYrd2974H9KbiGa5/0=
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4/go.mod h1:WSY9Jj5RhdgC3ci1QaacvbFdQ8cbrEjrpiZbLHLt2s4=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20241108202711-f7e3563b0271 h1:TPj0pMLCTy1CKwmrat3hqTxoZfqOuTy0asG0ccpGk8Q=
github.com/opencontainers/runtime-tools v0.9.1-0.20241108202711-f7e3563b0271/go.mod h1:oIH6VwKkaDOO+SIYZpdwrC/0wKYqrfO6E1sG1j3UVws=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/seccomp/containers-golang v0.6.0 h1:VWPMMIDr8pAtNjCX0WvLEEK9EQi5lAm4HtJbDtAtFvQ=
github.com/seccomp/containers-golang v0.6.0/go.mod h1:Dd9mONHvW4YdbSzdm23yf2CFw0iqvqLhO0mEFvPIvm4=
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/seccomp/libseccomp-golang v0.10.0 h1:aA4bp+/Zzi0BnWZ2F1wgNBs5gTpm+na2rWM6M9YjLpY=
github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243 h1:R43TdZy32XXSXjJn7M/HhALJ9imq6ztLnChfYJpVDnM=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8=
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,217 +1,64 @@
#!/usr/bin/env bash
#
# For help and usage information, simply execute the script w/o any arguments.
#
# This script is intended to be run by Red Hat oci-seccomp-bpf-hook developers
# who need to debug problems specifically related to Cirrus-CI automated testing.
# It requires that you have been granted prior access to create VMs in
# google-cloud. For non-Red Hat contributors, VMs are available as-needed,
# with supervision upon request.
set -e
RED="\e[1;36;41m"
YEL="\e[1;33;44m"
NOR="\e[0m"
USAGE_WARNING="
${YEL}WARNING: This will not work without local sudo access to run podman,${NOR}
${YEL}and prior authorization to use the oci-seccomp-bpf-hook GCP project. Also,${NOR}
${YEL}possession of the proper ssh private key is required.${NOR}
"
# TODO: Many/most of these values should come from .cirrus.yml
ZONE="us-central1-c"
CPUS="2"
MEMORY="4Gb"
DISK="200"
PROJECT="oci-seccomp-bpf-hook"
GOSRC="/var/tmp/go/src/github.com/containers/oci-seccomp-bpf-hook"
GCLOUD_IMAGE=${GCLOUD_IMAGE:-quay.io/cevich/gcloud_centos:latest}
GCLOUD_SUDO=${GCLOUD_SUDO-sudo}
SSHUSER="root"
SCRIPT_FILEPATH=$(realpath "${BASH_SOURCE[0]}")
SCRIPT_DIRPATH=$(dirname "$SCRIPT_FILEPATH")
REPO_DIRPATH=$(realpath "$SCRIPT_DIRPATH/../")
# Shared tmp directory between container and us
TMPDIR=$(mktemp -d --tmpdir $(basename $0)_tmpdir_XXXXXX)
SECCOMPHOOKROOT=$(realpath "$(dirname $0)/../")
# else: Assume $PWD is the root of the oci-seccomp-bpf-hook repository
[[ "$SECCOMPHOOKROOT" != "/" ]] || SECCOMPHOOKROOT=$PWD
# Command shortcuts save some typing (assumes $SECCOMPHOOKROOT is subdir of $HOME)
PGCLOUD="$GCLOUD_SUDO podman run -it --rm -e AS_ID=$UID -e AS_USER=$USER --security-opt label=disable -v $TMPDIR:$HOME -v $HOME/.config/gcloud:$HOME/.config/gcloud -v $HOME/.config/gcloud/ssh:$HOME/.ssh -v $SECCOMPHOOKROOT:$SECCOMPHOOKROOT $GCLOUD_IMAGE --configuration=oci-seccomp-bpf-hook --project=$PROJECT"
SCP_CMD="$PGCLOUD compute scp --zone=$ZONE"
showrun() {
if [[ "$1" == "--background" ]]
then
shift
# Properly escape any nested spaces, so command can be copy-pasted
echo '+ '$(printf " %q" "$@")' &' > /dev/stderr
"$@" &
echo -e "${RED}<backgrounded>${NOR}"
else
echo '+ '$(printf " %q" "$@") > /dev/stderr
"$@"
# Help detect if we were called by get_ci_vm container
GET_CI_VM="${GET_CI_VM:-0}"
in_get_ci_vm() {
if ((GET_CI_VM==0)); then
echo "Error: $1 is not intended for use in this context"
exit 2
fi
}
cleanup() {
RET=$?
set +e
wait
# set GCLOUD_DEBUG to leave tmpdir behind for postmortem
test -z "$GCLOUD_DEBUG" && rm -rf $TMPDIR
# Not always called from an exit handler, but should always exit when called
exit $RET
}
trap cleanup EXIT
delvm() {
echo -e "\n"
echo -e "\n${YEL}Offering to Delete $VMNAME ${RED}(Might take a minute or two)${NOR}"
echo -e "\n${YEL}Note: It's safe to answer N, then re-run script again later.${NOR}"
showrun $CLEANUP_CMD # prompts for Yes/No
cleanup
}
image_hints() {
_BIS=$(egrep -m 1 '_BUILT_IMAGE_SUFFIX:[[:space:]+"[[:print:]]+"' \
"$SECCOMPHOOKROOT/.cirrus.yml" | cut -d: -f 2 | tr -d '"[:blank:]')
egrep '[[:space:]]+[[:alnum:]].+_CACHE_IMAGE_NAME:[[:space:]+"[[:print:]]+"' \
"$SECCOMPHOOKROOT/.cirrus.yml" | cut -d: -f 2 | tr -d '"[:blank:]' | \
sed -r -e "s/\\\$[{]_BUILT_IMAGE_SUFFIX[}]/$_BIS/" | sort -u
}
show_usage() {
echo -e "\n${RED}ERROR: $1${NOR}"
echo -e "${YEL}Usage: $(basename $0) <image_name>${NOR}"
echo ""
if [[ -r ".cirrus.yml" ]]
then
echo -e "${YEL}Some possible image_name values (from .cirrus.yml):${NOR}"
image_hints
echo ""
fi
exit 1
}
get_env_vars() {
python3 -c '
import yaml
env=yaml.load(open(".cirrus.yml"), Loader=yaml.SafeLoader)["env"]
keys=[k for k in env if "ENCRYPTED" not in str(env[k])]
for k,v in env.items():
v=str(v)
if "ENCRYPTED" not in v:
print("{0}=\"{1}\"".format(k, v))
'
}
parse_args(){
echo -e "$USAGE_WARNING"
if [[ "$USER" =~ "root" ]]
then
show_usage "This script must be run as a regular user."
fi
ENVS="$(get_env_vars)"
IMAGE_NAME="$1"
if [[ -z "$IMAGE_NAME" ]]
then
show_usage "No image-name specified."
fi
ENVS="$ENVS SPECIALMODE=\"$SPECIALMODE\""
SETUP_CMD="env $ENVS $GOSRC/contrib/cirrus/setup.sh"
VMNAME="${VMNAME:-${USER}-${IMAGE_NAME}}"
CREATE_CMD="$PGCLOUD compute instances create --zone=$ZONE --image-project=libpod-218412 --image=${IMAGE_NAME} --custom-cpu=$CPUS --custom-memory=$MEMORY --boot-disk-size=$DISK --labels=in-use-by=$USER $VMNAME"
SSH_CMD="$PGCLOUD compute ssh --zone=$ZONE $SSHUSER@$VMNAME"
CLEANUP_CMD="$PGCLOUD compute instances delete --zone=$ZONE --delete-disks=all $VMNAME"
}
##### main
[[ "${SECCOMPHOOKROOT%%${SECCOMPHOOKROOT##$HOME}}" == "$HOME" ]] || \
show_usage "Repo clone must be sub-dir of $HOME: $SECCOMPHOOKROOT"
cd "$SECCOMPHOOKROOT"
parse_args "$@"
# Ensure mount-points and data directories exist on host as $USER. Also prevents
# permission-denied errors during cleanup() b/c `sudo podman` created mount-points
# owned by root.
mkdir -p $TMPDIR/${SECCOMPHOOKROOT##$HOME}
mkdir -p $TMPDIR/.ssh
mkdir -p {$HOME,$TMPDIR}/.config/gcloud/ssh
chmod 700 {$HOME,$TMPDIR}/.config/gcloud/ssh $TMPDIR/.ssh
cd $SECCOMPHOOKROOT
# Attempt to determine if named 'oci-seccomp-bpf-hook' gcloud configuration exists
showrun $PGCLOUD info > $TMPDIR/gcloud-info
if egrep -q "Account:.*None" $TMPDIR/gcloud-info
then
echo -e "\n${YEL}WARNING: Can't find gcloud configuration for 'oci-seccomp-bpf-hook', running init.${NOR}"
echo -e " ${RED}Please choose '#1: Re-initialize' and 'login' if asked.${NOR}"
echo -e " ${RED}Please set Compute Region and Zone (if asked) to 'us-central1-b'.${NOR}"
echo -e " ${RED}DO NOT set any password for the generated ssh key.${NOR}"
showrun $PGCLOUD init --project=$PROJECT --console-only --skip-diagnostics
# Verify it worked (account name == someone@example.com)
$PGCLOUD info > $TMPDIR/gcloud-info-after-init
if egrep -q "Account:.*None" $TMPDIR/gcloud-info-after-init
then
echo -e "${RED}ERROR: Could not initialize 'oci-seccomp-bpf-hook' configuration in gcloud.${NOR}"
exit 5
fi
# If this is the only config, make it the default to avoid persistent warnings from gcloud
[[ -r "$HOME/.config/gcloud/configurations/config_default" ]] || \
ln "$HOME/.config/gcloud/configurations/config_oci-seccomp-bpf-hook" \
"$HOME/.config/gcloud/configurations/config_default"
# get_ci_vm APIv1 container entrypoint calls into this script
# to obtain required repo. specific configuration options.
if [[ "$1" == "--config" ]]; then
in_get_ci_vm "$1"
cat <<EOF
DESTDIR="/var/tmp/go/src/github.com/containers/oci-seccomp-bpf-hook"
UPSTREAM_REPO="https://github.com/containers/oci-seccomp-bpf-hook.git"
GCLOUD_PROJECT="oci-seccomp-bpf-hook"
GCLOUD_IMGPROJECT="libpod-218412"
GCLOUD_CFG="oci-seccomp-bpf-hook"
GCLOUD_ZONE="${GCLOUD_ZONE:-us-central1-c}"
GCLOUD_CPUS="2"
GCLOUD_MEMORY="4Gb"
GCLOUD_DISK="200"
EOF
elif [[ "$1" == "--setup" ]]; then
in_get_ci_vm "$1"
# get_ci_vm container entrypoint calls us with this option on the
# Cirrus-CI environment instance, to perform repo.-specific setup.
cd $REPO_DIRPATH
echo "+ Loading ./contrib/cirrus/lib.sh" > /dev/stderr
source ./contrib/cirrus/lib.sh
echo "+ Running environment setup" > /dev/stderr
./contrib/cirrus/setup.sh
else
# Create and access VM for specified Cirrus-CI task
mkdir -p $HOME/.config/gcloud/ssh
podman run -it --rm \
--tz=local \
-e NAME="$USER" \
-e SRCDIR=/src \
-e GCLOUD_ZONE="$GCLOUD_ZONE" \
-e DEBUG="${DEBUG:-0}" \
-v $REPO_DIRPATH:/src:O \
-v $HOME/.config/gcloud:/root/.config/gcloud:z \
-v $HOME/.config/gcloud/ssh:/root/.ssh:z \
quay.io/libpod/get_ci_vm:latest "$@"
fi
# Couldn't make rsync work with gcloud's ssh wrapper: ssh-keys generated on the fly
TARBALL=$VMNAME.tar.bz2
echo -e "\n${YEL}Packing up local repository into a tarball.${NOR}"
showrun --background tar cjf $TMPDIR/$TARBALL --warning=no-file-changed --exclude-vcs-ignores -C $SECCOMPHOOKROOT .
trap delvm INT # Allow deleting VM if CTRL-C during create
# This fails if VM already exists: permit this usage to re-init
echo -e "\n${YEL}Trying to create a VM named $VMNAME\n${RED}(might take a minute/two. Errors ignored).${NOR}"
showrun $CREATE_CMD || true # allow re-running commands below when "delete: N"
# Any subsequent failure should prompt for VM deletion
trap delvm EXIT
echo -e "\n${YEL}Retrying for 30s for ssh port to open (may give some errors)${NOR}"
trap 'COUNT=9999' INT
ATTEMPTS=10
for (( COUNT=1 ; COUNT <= $ATTEMPTS ; COUNT++ ))
do
if $SSH_CMD --command "true"; then break; else sleep 3s; fi
done
if (( COUNT > $ATTEMPTS ))
then
echo -e "\n${RED}Failed${NOR}"
exit 7
fi
echo -e "${YEL}Got it${NOR}"
echo -e "\n${YEL}Removing and re-creating $GOSRC on $VMNAME.${NOR}"
showrun $SSH_CMD --command "rm -rf $GOSRC"
showrun $SSH_CMD --command "mkdir -p $GOSRC"
echo -e "\n${YEL}Transferring tarball to $VMNAME.${NOR}"
wait
showrun $SCP_CMD $HOME/$TARBALL $SSHUSER@$VMNAME:/tmp/$TARBALL
echo -e "\n${YEL}Unpacking tarball into $GOSRC on $VMNAME.${NOR}"
showrun $SSH_CMD --command "tar xjf /tmp/$TARBALL -C $GOSRC"
echo -e "\n${YEL}Removing tarball on $VMNAME.${NOR}"
showrun $SSH_CMD --command "rm -f /tmp/$TARBALL"
echo -e "\n${YEL}Executing environment setup${NOR}"
showrun $SSH_CMD --command "$SETUP_CMD"
VMIP=$($PGCLOUD compute instances describe $VMNAME --format='get(networkInterfaces[0].accessConfigs[0].natIP)')
echo -e "\n${YEL}Connecting to $VMNAME${NOR}\nPublic IP Address: $VMIP\n${RED}(option to delete VM upon logout).${NOR}\n"
showrun $SSH_CMD -- -t "cd $GOSRC && exec env $ENVS bash -il"

View File

@ -5,9 +5,9 @@ import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"log/syslog"
"os"
"os/exec"
@ -21,10 +21,10 @@ import (
"syscall"
"time"
types "github.com/containers/common/pkg/seccomp"
"github.com/containers/storage/pkg/unshare"
"github.com/iovisor/gobpf/bcc"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
types "github.com/seccomp/containers-golang"
seccomp "github.com/seccomp/libseccomp-golang"
"github.com/sirupsen/logrus"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
@ -57,6 +57,11 @@ func main() {
logrus.AddHook(hook)
}
if os.Getuid() != 0 || unshare.IsRootless() {
logrus.Errorf("running the hook requires root privileges")
os.Exit(1)
}
runBPF := flag.Int("r", 0, "Trace the specified PID")
outputFile := flag.String("o", "", "Path of the output file")
inputFile := flag.String("i", "", "Path of the input file")
@ -124,7 +129,7 @@ func detachAndTrace() error {
// Sanity check the PID.
if s.Pid <= 0 {
return errors.Errorf("invalid PID %d (must be greater than 0)", s.Pid)
return fmt.Errorf("invalid PID %d (must be greater than 0)", s.Pid)
}
// Parse the State's annotation.
@ -159,12 +164,12 @@ func detachAndTrace() error {
executable, err := os.Executable()
if err != nil {
return errors.Wrap(err, "cannot determine executable")
return fmt.Errorf("cannot determine executable: %v", err)
}
process, err := os.StartProcess(executable, []string{"oci-seccomp-bpf-hook", "-r", strconv.Itoa(s.Pid), "-o", outputFile, "-i", inputFile}, attr)
if err != nil {
return errors.Wrap(err, "cannot re-execute")
return fmt.Errorf("cannot re-execute: %v", err)
}
defer func() {
if err := process.Release(); err != nil {
@ -183,7 +188,7 @@ func detachAndTrace() error {
case syscall.SIGUSR2:
return errors.New("error while tracing")
default:
return errors.Errorf("unexpected signal %v", s)
return fmt.Errorf("unexpected signal %v", s)
}
// The timeout kicked in. Kill the child and return the sad news.
@ -191,7 +196,7 @@ func detachAndTrace() error {
if err := process.Kill(); err != nil {
logrus.Errorf("error killing child process: %v", err)
}
return errors.Errorf("BPF program didn't compile and attach within %d seconds", BPFTimeout)
return fmt.Errorf("BPF program didn't compile and attach within %d seconds", BPFTimeout)
}
return nil
@ -204,7 +209,7 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
ppid := os.Getppid()
parentProcess, err := os.FindProcess(ppid)
if err != nil {
return errors.Wrapf(err, "cannot find parent process %d", ppid)
return fmt.Errorf("cannot find parent process %d: %v", ppid, err)
}
logrus.Infof("Running floating process PID to attach: %d", pid)
@ -223,37 +228,30 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
m := bcc.NewModule(src, []string{})
defer m.Close()
logrus.Info("Loading enter tracepoint")
enterTrace, err := m.LoadTracepoint("enter_trace")
if err != nil {
return errors.Wrap(err, "error loading tracepoint")
}
logrus.Info("Loading exit tracepoint")
checkExit, err := m.LoadTracepoint("check_exit")
if err != nil {
return errors.Wrap(err, "error loading tracepoint")
}
logrus.Info("Loaded tracepoints")
if err := m.AttachTracepoint("raw_syscalls:sys_enter", enterTrace); err != nil {
return errors.Wrap(err, "error attaching to tracepoint")
}
if err := m.AttachTracepoint("sched:sched_process_exit", checkExit); err != nil {
return errors.Wrap(err, "error attaching to tracepoint")
}
// Send a signal to the parent process to indicate the compilation has
// been completed.
if err := parentProcess.Signal(syscall.SIGUSR1); err != nil {
return err
}
signaledParent = true
table := bcc.NewTable(m.TableId("events"), m)
channel := make(chan []byte)
perfMap, err := bcc.InitPerfMap(table, channel, nil)
if err != nil {
return errors.Wrap(err, "error initializing perf map")
return fmt.Errorf("error initializing perf map: %v", err)
}
logrus.Info("Loading enter tracepoint")
enterTrace, err := m.LoadTracepoint("enter_trace")
if err != nil {
return fmt.Errorf("error loading tracepoint: %v", err)
}
logrus.Info("Loading exit tracepoint")
checkExit, err := m.LoadTracepoint("check_exit")
if err != nil {
return fmt.Errorf("error loading tracepoint: %v", err)
}
logrus.Info("Loaded tracepoints")
if err := m.AttachTracepoint("raw_syscalls:sys_enter", enterTrace); err != nil {
return fmt.Errorf("error attaching to tracepoint: %v", err)
}
if err := m.AttachTracepoint("sched:sched_process_exit", checkExit); err != nil {
return fmt.Errorf("error attaching to tracepoint: %v", err)
}
// Initialize the wait group used to wait for the tracing to be finished.
@ -290,28 +288,26 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
logrus.Info("PerfMap Start")
perfMap.Start()
// Send a signal to the parent process to indicate the compilation has
// been completed.
if err := parentProcess.Signal(syscall.SIGUSR1); err != nil {
return err
}
signaledParent = true
// Waiting for the goroutine which is reading the perf buffer to be done
// The goroutine will exit when the container exits
wg.Wait()
logrus.Info("BPF program has finished")
// Post-process the recorded events and extract the syscall names.
recordSyscalls := false
for _, e := range events {
name, err := syscallIDtoName(e.ID)
if err != nil {
logrus.Errorf("error getting the name for syscall ID %d", e.ID)
continue
}
// Syscalls are not recorded until prctl() is called. The first
// invocation of prctl is guaranteed to happen by the supported
// OCI runtimes (i.e., runc and crun) as it's being called when
// setting the seccomp profile.
if name == "prctl" {
recordSyscalls = true
}
if recordSyscalls {
syscalls[name]++
}
syscalls[name]++
}
logrus.Info("PerfMap Stop")
@ -319,7 +315,7 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
logrus.Infof("Writing seccomp profile to %q", profilePath)
if err := generateProfile(syscalls, profilePath, inputFile); err != nil {
return errors.Wrap(err, "error generating final seccomp profile")
return fmt.Errorf("error generating final seccomp profile: %v", err)
}
return nil
}
@ -331,13 +327,13 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
inputProfile := types.Seccomp{}
if inputFile != "" {
input, err := ioutil.ReadFile(inputFile)
input, err := os.ReadFile(inputFile)
if err != nil {
return errors.Wrap(err, "error reading input file")
return fmt.Errorf("error reading input file: %v", err)
}
err = json.Unmarshal(input, &inputProfile)
if err != nil {
return errors.Wrap(err, "error parsing input file")
return fmt.Errorf("error parsing input file: %v", err)
}
}
@ -355,7 +351,7 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
outputProfile.DefaultAction = types.ActErrno
if err := appendArchIfNotAlreadyIncluded(runtime.GOARCH, &outputProfile); err != nil {
return errors.Wrap(err, "appending architecture to output profile")
return fmt.Errorf("appending architecture to output profile: %v", err)
}
outputProfile.Syscalls = append(outputProfile.Syscalls, &types.Syscall{
@ -366,10 +362,10 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
sJSON, err := json.Marshal(outputProfile)
if err != nil {
return errors.Wrap(err, "error writing seccomp profile")
return fmt.Errorf("error writing seccomp profile: %v", err)
}
if err := ioutil.WriteFile(profilePath, sJSON, 0644); err != nil {
return errors.Wrap(err, "error writing seccomp profile")
if err := os.WriteFile(profilePath, sJSON, 0644); err != nil {
return fmt.Errorf("error writing seccomp profile: %v", err)
}
return nil
}
@ -379,7 +375,7 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
func parseAnnotation(annotation string) (outputFile string, inputFile string, err error) {
annotationSplit := strings.Split(annotation, ";")
if len(annotationSplit) > 2 {
return "", "", errors.Wrapf(errInvalidAnnotation, "more than one semi-colon: %q", annotation)
return "", "", fmt.Errorf("%v: more than one semi-colon: %q", errInvalidAnnotation, annotation)
}
for _, path := range annotationSplit {
switch {
@ -387,33 +383,33 @@ func parseAnnotation(annotation string) (outputFile string, inputFile string, er
case strings.HasPrefix(path, "if:"):
inputFile = strings.TrimSpace(strings.TrimPrefix(path, InputPrefix))
if !filepath.IsAbs(inputFile) {
return "", "", errors.Wrapf(errInvalidAnnotation, "input file path must be absolute: %q", inputFile)
return "", "", fmt.Errorf("%v: input file path must be absolute: %q", errInvalidAnnotation, inputFile)
}
inputProfile := types.Seccomp{}
input, err := ioutil.ReadFile(inputFile)
input, err := os.ReadFile(inputFile)
if err != nil {
return "", "", errors.Wrapf(errInvalidAnnotation, "error reading input file: %q", inputFile)
return "", "", fmt.Errorf("%v: error reading input file: %q", errInvalidAnnotation, inputFile)
}
err = json.Unmarshal(input, &inputProfile)
if err != nil {
return "", "", errors.Wrapf(errInvalidAnnotation, "error parsing input file: %q", inputFile)
return "", "", fmt.Errorf("%v: error parsing input file: %q", errInvalidAnnotation, inputFile)
}
// Output profile
case strings.HasPrefix(path, "of:"):
outputFile = strings.TrimSpace(strings.TrimPrefix(path, OutputPrefix))
if !filepath.IsAbs(outputFile) {
return "", "", errors.Wrapf(errInvalidAnnotation, "output file path must be absolute: %q", outputFile)
return "", "", fmt.Errorf("%v: output file path must be absolute: %q", errInvalidAnnotation, outputFile)
}
// Unsupported default
default:
return "", "", errors.Wrapf(errInvalidAnnotation, "must start %q or %q prefix", InputPrefix, OutputPrefix)
return "", "", fmt.Errorf("%v: must start %q or %q prefix", errInvalidAnnotation, InputPrefix, OutputPrefix)
}
}
if outputFile == "" {
return "", "", errors.Wrap(errInvalidAnnotation, "providing output file is mandatory")
return "", "", fmt.Errorf("%v: providing output file is mandatory", errInvalidAnnotation)
}
return outputFile, inputFile, nil
@ -442,7 +438,7 @@ func syscallInProfile(profile *types.Seccomp, syscall string) bool {
func appendArchIfNotAlreadyIncluded(goArch string, profile *types.Seccomp) error {
targetArch, err := types.GoArchToSeccompArch(goArch)
if err != nil {
return errors.Wrap(err, "determine target architecture")
return fmt.Errorf("determine target architecture: %v", err)
}
for _, arch := range profile.Architectures {
if arch == targetArch {

View File

@ -2,12 +2,11 @@ package main
import (
"encoding/json"
"io/ioutil"
"os"
"runtime"
"testing"
types "github.com/seccomp/containers-golang"
types "github.com/containers/common/pkg/seccomp"
"github.com/stretchr/testify/assert"
)
@ -15,7 +14,7 @@ func TestParseAnnotation(t *testing.T) {
testProfile := types.Seccomp{}
testProfile.DefaultAction = types.ActErrno
tmpFile, err := ioutil.TempFile(os.TempDir(), "input-*.json")
tmpFile, err := os.CreateTemp(os.TempDir(), "input-*.json")
if err != nil {
t.Fatalf("cannot create temporary file")
}

View File

@ -10,6 +10,13 @@ load helpers
}
@test "Version check" {
# First, we don't have a VERSION file in a system-test environment
# Second, /usr/libexec/oci/hooks.d/oci-seccomp-bpf-hook --version
# produces no output.
if [ ! -d .git ]; then
skip "This test only makes sense in a source-tree environment"
fi
local version
version=$(cat ./VERSION)
run ./bin/oci-seccomp-bpf-hook --version
@ -78,7 +85,7 @@ load helpers
echo "Podman output: ${lines[*]}"
[ "$status" -eq 0 ]
run podman run --net=host --security-opt seccomp=${tmpFile} ${ALPINE} ping -c3 google.com
run podman run --net=host --security-opt seccomp=${tmpFile} ${ALPINE} ping -c3 $PINGABLE_HOST
echo "Podman output: ${lines[*]}"
[ "$status" -ne 0 ]
}
@ -86,7 +93,7 @@ load helpers
@test "Extend existing seccomp profile" {
local tmpFile1
local tmpFile2
local size
local size
tmpFile1=$(mktemp)
tmpFile2=$(mktemp)
@ -102,15 +109,15 @@ load helpers
echo "Size of the first generated file: ${size}"
[ "${size}" -gt 0 ]
run podman run --net=host --security-opt seccomp=${tmpFile1} ${ALPINE} ping -c3 google.com
run podman run --net=host --security-opt seccomp=${tmpFile1} ${ALPINE} ping -c3 $PINGABLE_HOST
echo "Podman output: ${lines[*]}"
[ "$status" -ne 0 ]
run podman run --net=host --annotation io.containers.trace-syscall="if:${tmpFile1};of:${tmpFile2}" ${ALPINE} ping -c3 google.com
run podman run --net=host --annotation io.containers.trace-syscall="if:${tmpFile1};of:${tmpFile2}" ${ALPINE} ping -c3 $PINGABLE_HOST
echo "Podman output: ${lines[*]}"
[ "$status" -eq 0 ]
sleep 2 # sleep two seconds to let the hook finish writing the file
size=$(du -b ${tmpFile2} | awk '{ print $1 }')
echo "Size of the second generated file: ${size}"
[ "${size}" -gt 0 ]
@ -119,14 +126,14 @@ load helpers
echo "Podman output: ${lines[*]}"
[ "$status" -eq 0 ]
run podman run --net=host --security-opt seccomp=${tmpFile2} ${ALPINE} ping -c3 google.com
run podman run --net=host --security-opt seccomp=${tmpFile2} ${ALPINE} ping -c3 $PINGABLE_HOST
echo "Podman output: ${lines[*]}"
[ "$status" -eq 0 ]
}
@test "Syscall blocked in input profile remains blocked in output profile" {
local tmpFile
local size
local size
tmpFile=$(mktemp)
echo "Temporary file : ${tmpFile}"

View File

@ -1,2 +1,5 @@
ALPINE="docker.io/library/alpine:latest"
BLOCK_MKDIR=$(realpath ./test/fixtures/block-mkdir.json)
ALPINE="quay.io/libpod/alpine:latest"
BLOCK_MKDIR=$(realpath $(dirname ${BASH_SOURCE[0]})/fixtures/block-mkdir.json)
# Hostname that should be ping'able from any environment in which we run tests
PINGABLE_HOST=github.com

View File

@ -0,0 +1,197 @@
// NOTE: this package has originally been copied from
// github.com/opencontainers/runc and modified to work for other use cases
package seccomp
import (
"fmt"
"github.com/opencontainers/runtime-spec/specs-go"
)
var (
goArchToSeccompArchMap = map[string]Arch{
"386": ArchX86,
"amd64": ArchX86_64,
"amd64p32": ArchX32,
"arm": ArchARM,
"arm64": ArchAARCH64,
"mips": ArchMIPS,
"mips64": ArchMIPS64,
"mips64le": ArchMIPSEL64,
"mips64p32": ArchMIPS64N32,
"mips64p32le": ArchMIPSEL64N32,
"mipsle": ArchMIPSEL,
"ppc": ArchPPC,
"ppc64": ArchPPC64,
"ppc64le": ArchPPC64LE,
"s390": ArchS390,
"s390x": ArchS390X,
}
specArchToLibseccompArchMap = map[specs.Arch]string{
specs.ArchX86: "x86",
specs.ArchX86_64: "amd64",
specs.ArchX32: "x32",
specs.ArchARM: "arm",
specs.ArchAARCH64: "arm64",
specs.ArchMIPS: "mips",
specs.ArchMIPS64: "mips64",
specs.ArchMIPS64N32: "mips64n32",
specs.ArchMIPSEL: "mipsel",
specs.ArchMIPSEL64: "mipsel64",
specs.ArchMIPSEL64N32: "mipsel64n32",
specs.ArchPPC: "ppc",
specs.ArchPPC64: "ppc64",
specs.ArchPPC64LE: "ppc64le",
specs.ArchS390: "s390",
specs.ArchS390X: "s390x",
}
specArchToSeccompArchMap = map[specs.Arch]Arch{
specs.ArchX86: ArchX86,
specs.ArchX86_64: ArchX86_64,
specs.ArchX32: ArchX32,
specs.ArchARM: ArchARM,
specs.ArchAARCH64: ArchAARCH64,
specs.ArchMIPS: ArchMIPS,
specs.ArchMIPS64: ArchMIPS64,
specs.ArchMIPS64N32: ArchMIPS64N32,
specs.ArchMIPSEL: ArchMIPSEL,
specs.ArchMIPSEL64: ArchMIPSEL64,
specs.ArchMIPSEL64N32: ArchMIPSEL64N32,
specs.ArchPPC: ArchPPC,
specs.ArchPPC64: ArchPPC64,
specs.ArchPPC64LE: ArchPPC64LE,
specs.ArchS390: ArchS390,
specs.ArchS390X: ArchS390X,
}
specActionToSeccompActionMap = map[specs.LinuxSeccompAction]Action{
specs.ActKill: ActKill,
// TODO: wait for this PR to get merged:
// https://github.com/opencontainers/runtime-spec/pull/1064
// specs.ActKillProcess ActKillProcess,
// specs.ActKillThread ActKillThread,
specs.ActErrno: ActErrno,
specs.ActTrap: ActTrap,
specs.ActAllow: ActAllow,
specs.ActTrace: ActTrace,
specs.ActLog: ActLog,
specs.ActNotify: ActNotify,
}
specOperatorToSeccompOperatorMap = map[specs.LinuxSeccompOperator]Operator{
specs.OpNotEqual: OpNotEqual,
specs.OpLessThan: OpLessThan,
specs.OpLessEqual: OpLessEqual,
specs.OpEqualTo: OpEqualTo,
specs.OpGreaterEqual: OpGreaterEqual,
specs.OpGreaterThan: OpGreaterThan,
specs.OpMaskedEqual: OpMaskedEqual,
}
)
// GoArchToSeccompArch converts a runtime.GOARCH to a seccomp `Arch`. The
// function returns an error if the architecture conversion is not supported.
func GoArchToSeccompArch(goArch string) (Arch, error) {
arch, ok := goArchToSeccompArchMap[goArch]
if !ok {
return "", fmt.Errorf("unsupported go arch provided: %s", goArch)
}
return arch, nil
}
// specToSeccomp converts a `LinuxSeccomp` spec into a `Seccomp` struct.
func specToSeccomp(spec *specs.LinuxSeccomp) (*Seccomp, error) {
res := &Seccomp{
Syscalls: []*Syscall{},
}
for _, arch := range spec.Architectures {
newArch, err := specArchToSeccompArch(arch)
if err != nil {
return nil, fmt.Errorf("convert spec arch: %w", err)
}
res.Architectures = append(res.Architectures, newArch)
}
// Convert default action
newDefaultAction, err := specActionToSeccompAction(spec.DefaultAction)
if err != nil {
return nil, fmt.Errorf("convert default action: %w", err)
}
res.DefaultAction = newDefaultAction
res.DefaultErrnoRet = spec.DefaultErrnoRet
// Loop through all syscall blocks and convert them to the internal format
for _, call := range spec.Syscalls {
newAction, err := specActionToSeccompAction(call.Action)
if err != nil {
return nil, fmt.Errorf("convert action: %w", err)
}
for _, name := range call.Names {
newCall := Syscall{
Name: name,
Action: newAction,
ErrnoRet: call.ErrnoRet,
Args: []*Arg{},
}
// Loop through all the arguments of the syscall and convert them
for _, arg := range call.Args {
newOp, err := specOperatorToSeccompOperator(arg.Op)
if err != nil {
return nil, fmt.Errorf("convert operator: %w", err)
}
newArg := Arg{
Index: arg.Index,
Value: arg.Value,
ValueTwo: arg.ValueTwo,
Op: newOp,
}
newCall.Args = append(newCall.Args, &newArg)
}
res.Syscalls = append(res.Syscalls, &newCall)
}
}
return res, nil
}
// specArchToLibseccompArch converts a spec arch into a libseccomp one.
func specArchToLibseccompArch(arch specs.Arch) (string, error) {
if res, ok := specArchToLibseccompArchMap[arch]; ok {
return res, nil
}
return "", fmt.Errorf(
"architecture %q is not valid for libseccomp", arch,
)
}
// specArchToSeccompArch converts a spec arch into an internal one.
func specArchToSeccompArch(arch specs.Arch) (Arch, error) {
if res, ok := specArchToSeccompArchMap[arch]; ok {
return res, nil
}
return "", fmt.Errorf("architecture %q is not valid", arch)
}
// specActionToSeccompAction converts a spec action into a seccomp one.
func specActionToSeccompAction(action specs.LinuxSeccompAction) (Action, error) {
if res, ok := specActionToSeccompActionMap[action]; ok {
return res, nil
}
return "", fmt.Errorf(
"spec action %q is not valid internal action", action,
)
}
// specOperatorToSeccompOperator converts a spec operator into a seccomp one.
func specOperatorToSeccompOperator(operator specs.LinuxSeccompOperator) (Operator, error) {
if op, ok := specOperatorToSeccompOperatorMap[operator]; ok {
return op, nil
}
return "", fmt.Errorf(
"spec operator %q is not a valid internal operator", operator,
)
}

View File

@ -1,14 +1,10 @@
// +build seccomp
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2018 Docker, Inc.
package seccomp // import "github.com/seccomp/containers-golang"
package seccomp
import (
"syscall"
"golang.org/x/sys/unix"
)
@ -47,9 +43,54 @@ func arches() []Architecture {
// DefaultProfile defines the allowlist for the default seccomp profile.
func DefaultProfile() *Seccomp {
einval := uint(syscall.EINVAL)
einval := uint(unix.EINVAL)
enosys := uint(unix.ENOSYS)
eperm := uint(unix.EPERM)
syscalls := []*Syscall{
{
Names: []string{
"bdflush",
"cachestat",
"futex_requeue",
"futex_wait",
"futex_waitv",
"futex_wake",
"io_pgetevents",
"io_pgetevents_time64",
"kexec_file_load",
"kexec_load",
"map_shadow_stack",
"migrate_pages",
"move_pages",
"nfsservctl",
"nice",
"oldfstat",
"oldlstat",
"oldolduname",
"oldstat",
"olduname",
"pciconfig_iobase",
"pciconfig_read",
"pciconfig_write",
"sgetmask",
"ssetmask",
"swapoff",
"swapon",
"syscall",
"sysfs",
"uselib",
"userfaultfd",
"ustat",
"vm86",
"vm86old",
"vmsplice",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
},
{
Names: []string{
"_llseek",
@ -67,10 +108,18 @@ func DefaultProfile() *Seccomp {
"chmod",
"chown",
"chown32",
"clock_adjtime",
"clock_adjtime64",
"clock_getres",
"clock_getres_time64",
"clock_gettime",
"clock_gettime64",
"clock_nanosleep",
"clock_nanosleep_time64",
"clone",
"clone3",
"close",
"close_range",
"connect",
"copy_file_range",
"creat",
@ -82,6 +131,7 @@ func DefaultProfile() *Seccomp {
"epoll_ctl",
"epoll_ctl_old",
"epoll_pwait",
"epoll_pwait2",
"epoll_wait",
"epoll_wait_old",
"eventfd",
@ -91,6 +141,7 @@ func DefaultProfile() *Seccomp {
"exit",
"exit_group",
"faccessat",
"faccessat2",
"fadvise64",
"fadvise64_64",
"fallocate",
@ -98,6 +149,7 @@ func DefaultProfile() *Seccomp {
"fchdir",
"fchmod",
"fchmodat",
"fchmodat2",
"fchown",
"fchown32",
"fchownat",
@ -109,7 +161,11 @@ func DefaultProfile() *Seccomp {
"flock",
"fork",
"fremovexattr",
"fsconfig",
"fsetxattr",
"fsmount",
"fsopen",
"fspick",
"fstat",
"fstat64",
"fstatat64",
@ -119,7 +175,9 @@ func DefaultProfile() *Seccomp {
"ftruncate",
"ftruncate64",
"futex",
"futex_time64",
"futimesat",
"get_mempolicy",
"get_robust_list",
"get_thread_area",
"getcpu",
@ -169,7 +227,11 @@ func DefaultProfile() *Seccomp {
"ioprio_get",
"ioprio_set",
"ipc",
"keyctl",
"kill",
"landlock_add_rule",
"landlock_create_ruleset",
"landlock_restrict_self",
"lchown",
"lchown32",
"lgetxattr",
@ -184,7 +246,10 @@ func DefaultProfile() *Seccomp {
"lstat",
"lstat64",
"madvise",
"mbind",
"membarrier",
"memfd_create",
"memfd_secret",
"mincore",
"mkdir",
"mkdirat",
@ -196,12 +261,16 @@ func DefaultProfile() *Seccomp {
"mmap",
"mmap2",
"mount",
"mount_setattr",
"move_mount",
"mprotect",
"mq_getsetattr",
"mq_notify",
"mq_open",
"mq_timedreceive",
"mq_timedreceive_time64",
"mq_timedsend",
"mq_timedsend_time64",
"mq_unlink",
"mremap",
"msgctl",
@ -216,18 +285,33 @@ func DefaultProfile() *Seccomp {
"nanosleep",
"newfstatat",
"open",
"open_tree",
"openat",
"openat2",
"pause",
"pidfd_getfd",
"pidfd_open",
"pidfd_send_signal",
"pipe",
"pipe2",
"pivot_root",
"pkey_alloc",
"pkey_free",
"pkey_mprotect",
"poll",
"ppoll",
"ppoll_time64",
"prctl",
"pread64",
"preadv",
"preadv2",
"prlimit64",
"process_mrelease",
"process_vm_readv",
"process_vm_writev",
"pselect6",
"pselect6_time64",
"ptrace",
"pwrite64",
"pwritev",
"pwritev2",
@ -240,6 +324,7 @@ func DefaultProfile() *Seccomp {
"recv",
"recvfrom",
"recvmmsg",
"recvmmsg_time64",
"recvmsg",
"remap_file_pages",
"removexattr",
@ -248,6 +333,7 @@ func DefaultProfile() *Seccomp {
"renameat2",
"restart_syscall",
"rmdir",
"rseq",
"rt_sigaction",
"rt_sigpending",
"rt_sigprocmask",
@ -255,6 +341,7 @@ func DefaultProfile() *Seccomp {
"rt_sigreturn",
"rt_sigsuspend",
"rt_sigtimedwait",
"rt_sigtimedwait_time64",
"rt_tgsigqueueinfo",
"sched_get_priority_max",
"sched_get_priority_min",
@ -263,6 +350,7 @@ func DefaultProfile() *Seccomp {
"sched_getparam",
"sched_getscheduler",
"sched_rr_get_interval",
"sched_rr_get_interval_time64",
"sched_setaffinity",
"sched_setattr",
"sched_setparam",
@ -274,12 +362,14 @@ func DefaultProfile() *Seccomp {
"semget",
"semop",
"semtimedop",
"semtimedop_time64",
"send",
"sendfile",
"sendfile64",
"sendmmsg",
"sendmsg",
"sendto",
"set_mempolicy",
"set_robust_list",
"set_thread_area",
"set_tid_address",
@ -292,6 +382,7 @@ func DefaultProfile() *Seccomp {
"setgroups",
"setgroups32",
"setitimer",
"setns",
"setpgid",
"setpriority",
"setregid",
@ -314,8 +405,10 @@ func DefaultProfile() *Seccomp {
"shmget",
"shutdown",
"sigaltstack",
"signal",
"signalfd",
"signalfd4",
"sigprocmask",
"sigreturn",
"socketcall",
"socketpair",
@ -339,10 +432,14 @@ func DefaultProfile() *Seccomp {
"timer_delete",
"timer_getoverrun",
"timer_gettime",
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
"timerfd_settime",
"timerfd_settime64",
"times",
"tkill",
"truncate",
@ -357,9 +454,9 @@ func DefaultProfile() *Seccomp {
"unshare",
"utime",
"utimensat",
"utimensat_time64",
"utimes",
"vfork",
"vmsplice",
"wait4",
"waitid",
"waitpid",
@ -427,6 +524,7 @@ func DefaultProfile() *Seccomp {
{
Names: []string{
"sync_file_range2",
"swapcontext",
},
Action: ActAllow,
Args: []*Arg{},
@ -438,10 +536,10 @@ func DefaultProfile() *Seccomp {
Names: []string{
"arm_fadvise64_64",
"arm_sync_file_range",
"sync_file_range2",
"breakpoint",
"cacheflush",
"set_tls",
"sync_file_range2",
},
Action: ActAllow,
Args: []*Arg{},
@ -481,6 +579,16 @@ func DefaultProfile() *Seccomp {
Arches: []string{"s390", "s390x"},
},
},
{
Names: []string{
"riscv_flush_icache",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"riscv64"},
},
},
{
Names: []string{
"open_by_handle_at",
@ -491,22 +599,28 @@ func DefaultProfile() *Seccomp {
Caps: []string{"CAP_DAC_READ_SEARCH"},
},
},
{
Names: []string{
"open_by_handle_at",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_DAC_READ_SEARCH"},
},
},
{
Names: []string{
"bpf",
"clone",
"fanotify_init",
"lookup_dcookie",
"mount",
"name_to_handle_at",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
"umount",
"umount2",
"unshare",
},
Action: ActAllow,
Args: []*Arg{},
@ -516,53 +630,23 @@ func DefaultProfile() *Seccomp {
},
{
Names: []string{
"clone",
},
Action: ActAllow,
Args: []*Arg{
{
Index: 0,
Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET,
ValueTwo: 0,
Op: OpMaskedEqual,
},
},
Excludes: Filter{
Caps: []string{"CAP_SYS_ADMIN"},
Arches: []string{"s390", "s390x"},
},
},
{
Names: []string{
"clone",
},
Action: ActAllow,
Args: []*Arg{
{
Index: 1,
Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET,
ValueTwo: 0,
Op: OpMaskedEqual,
},
},
Comment: "s390 parameter ordering for clone is different",
Includes: Filter{
Arches: []string{"s390", "s390x"},
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_ADMIN"},
},
},
{
Names: []string{
"reboot",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_SYS_BOOT"},
},
},
{
Names: []string{
"chroot",
@ -573,11 +657,23 @@ func DefaultProfile() *Seccomp {
Caps: []string{"CAP_SYS_CHROOT"},
},
},
{
Names: []string{
"chroot",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_CHROOT"},
},
},
{
Names: []string{
"delete_module",
"init_module",
"finit_module",
"init_module",
"query_module",
},
Action: ActAllow,
@ -588,15 +684,17 @@ func DefaultProfile() *Seccomp {
},
{
Names: []string{
"get_mempolicy",
"mbind",
"name_to_handle_at",
"set_mempolicy",
"delete_module",
"finit_module",
"init_module",
"query_module",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_SYS_NICE"},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_MODULE"},
},
},
{
@ -609,12 +707,22 @@ func DefaultProfile() *Seccomp {
Caps: []string{"CAP_SYS_PACCT"},
},
},
{
Names: []string{
"acct",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_PACCT"},
},
},
{
Names: []string{
"kcmp",
"process_vm_readv",
"process_vm_writev",
"ptrace",
"process_madvise",
},
Action: ActAllow,
Args: []*Arg{},
@ -624,8 +732,21 @@ func DefaultProfile() *Seccomp {
},
{
Names: []string{
"iopl",
"kcmp",
"process_madvise",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_PTRACE"},
},
},
{
Names: []string{
"ioperm",
"iopl",
},
Action: ActAllow,
Args: []*Arg{},
@ -635,9 +756,23 @@ func DefaultProfile() *Seccomp {
},
{
Names: []string{
"ioperm",
"iopl",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_RAWIO"},
},
},
{
Names: []string{
"clock_settime",
"clock_settime64",
"settimeofday",
"stime",
"clock_settime",
},
Action: ActAllow,
Args: []*Arg{},
@ -645,6 +780,21 @@ func DefaultProfile() *Seccomp {
Caps: []string{"CAP_SYS_TIME"},
},
},
{
Names: []string{
"clock_settime",
"clock_settime64",
"settimeofday",
"stime",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_TIME"},
},
},
{
Names: []string{
"vhangup",
@ -655,21 +805,34 @@ func DefaultProfile() *Seccomp {
Caps: []string{"CAP_SYS_TTY_CONFIG"},
},
},
{
Names: []string{
"vhangup",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_TTY_CONFIG"},
},
},
{
Names: []string{
"socket",
},
Action: ActErrno,
Errno: "EINVAL",
ErrnoRet: &einval,
Args: []*Arg{
{
Index: 0,
Value: syscall.AF_NETLINK,
Value: unix.AF_NETLINK,
Op: OpEqualTo,
},
{
Index: 2,
Value: syscall.NETLINK_AUDIT,
Value: unix.NETLINK_AUDIT,
Op: OpEqualTo,
},
},
@ -685,7 +848,7 @@ func DefaultProfile() *Seccomp {
Args: []*Arg{
{
Index: 2,
Value: syscall.NETLINK_AUDIT,
Value: unix.NETLINK_AUDIT,
Op: OpNotEqual,
},
},
@ -701,7 +864,7 @@ func DefaultProfile() *Seccomp {
Args: []*Arg{
{
Index: 0,
Value: syscall.AF_NETLINK,
Value: unix.AF_NETLINK,
Op: OpNotEqual,
},
},
@ -717,7 +880,7 @@ func DefaultProfile() *Seccomp {
Args: []*Arg{
{
Index: 2,
Value: syscall.NETLINK_AUDIT,
Value: unix.NETLINK_AUDIT,
Op: OpNotEqual,
},
},
@ -734,11 +897,57 @@ func DefaultProfile() *Seccomp {
Caps: []string{"CAP_AUDIT_WRITE"},
},
},
{
Names: []string{
"bpf",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_ADMIN", "CAP_BPF"},
},
},
{
Names: []string{
"bpf",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_BPF"},
},
},
{
Names: []string{
"perf_event_open",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_ADMIN", "CAP_BPF"},
},
},
{
Names: []string{
"perf_event_open",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_PERFMON"},
},
},
}
return &Seccomp{
DefaultAction: ActErrno,
ArchMap: arches(),
Syscalls: syscalls,
DefaultAction: ActErrno,
DefaultErrno: "ENOSYS",
DefaultErrnoRet: &enosys,
ArchMap: arches(),
Syscalls: syscalls,
}
}

View File

@ -0,0 +1,93 @@
//go:build linux && seccomp
package seccomp
import (
"golang.org/x/sys/unix"
)
// Error table
var errnoArch = map[string]uint{
"EPERM": uint(unix.EPERM),
"ENOENT": uint(unix.ENOENT),
"ESRCH": uint(unix.ESRCH),
"EIO": uint(unix.EIO),
"ENXIO": uint(unix.ENXIO),
"E2BIG": uint(unix.E2BIG),
"ENOEXEC": uint(unix.ENOEXEC),
"EBADF": uint(unix.EBADF),
"ECHILD": uint(unix.ECHILD),
"EDEADLK": uint(unix.EDEADLK),
"ENOMEM": uint(unix.ENOMEM),
"EACCES": uint(unix.EACCES),
"EFAULT": uint(unix.EFAULT),
"ENOTBLK": uint(unix.ENOTBLK),
"EBUSY": uint(unix.EBUSY),
"EEXIST": uint(unix.EEXIST),
"EXDEV": uint(unix.EXDEV),
"ENODEV": uint(unix.ENODEV),
"ENOTDIR": uint(unix.ENOTDIR),
"EISDIR": uint(unix.EISDIR),
"EINVAL": uint(unix.EINVAL),
"ENFILE": uint(unix.ENFILE),
"EMFILE": uint(unix.EMFILE),
"ENOTTY": uint(unix.ENOTTY),
"ETXTBSY": uint(unix.ETXTBSY),
"EFBIG": uint(unix.EFBIG),
"ENOSPC": uint(unix.ENOSPC),
"ESPIPE": uint(unix.ESPIPE),
"EROFS": uint(unix.EROFS),
"EMLINK": uint(unix.EMLINK),
"EPIPE": uint(unix.EPIPE),
"EDOM": uint(unix.EDOM),
"ERANGE": uint(unix.ERANGE),
"EAGAIN": uint(unix.EAGAIN),
"EINPROGRESS": uint(unix.EINPROGRESS),
"EALREADY": uint(unix.EALREADY),
"ENOTSOCK": uint(unix.ENOTSOCK),
"EDESTADDRREQ": uint(unix.EDESTADDRREQ),
"EMSGSIZE": uint(unix.EMSGSIZE),
"EPROTOTYPE": uint(unix.EPROTOTYPE),
"ENOPROTOOPT": uint(unix.ENOPROTOOPT),
"EPROTONOSUPPORT": uint(unix.EPROTONOSUPPORT),
"ESOCKTNOSUPPORT": uint(unix.ESOCKTNOSUPPORT),
"EOPNOTSUPP": uint(unix.EOPNOTSUPP),
"EPFNOSUPPORT": uint(unix.EPFNOSUPPORT),
"EAFNOSUPPORT": uint(unix.EAFNOSUPPORT),
"EADDRINUSE": uint(unix.EADDRINUSE),
"EADDRNOTAVAIL": uint(unix.EADDRNOTAVAIL),
"ENETDOWN": uint(unix.ENETDOWN),
"ENETUNREACH": uint(unix.ENETUNREACH),
"ENETRESET": uint(unix.ENETRESET),
"ECONNABORTED": uint(unix.ECONNABORTED),
"ECONNRESET": uint(unix.ECONNRESET),
"ENOBUFS": uint(unix.ENOBUFS),
"EISCONN": uint(unix.EISCONN),
"ENOTCONN": uint(unix.ENOTCONN),
"ESHUTDOWN": uint(unix.ESHUTDOWN),
"ETOOMANYREFS": uint(unix.ETOOMANYREFS),
"ETIMEDOUT": uint(unix.ETIMEDOUT),
"ECONNREFUSED": uint(unix.ECONNREFUSED),
"ELOOP": uint(unix.ELOOP),
"ENAMETOOLONG": uint(unix.ENAMETOOLONG),
"EHOSTDOWN": uint(unix.EHOSTDOWN),
"EHOSTUNREACH": uint(unix.EHOSTUNREACH),
"ENOTEMPTY": uint(unix.ENOTEMPTY),
"EUSERS": uint(unix.EUSERS),
"EDQUOT": uint(unix.EDQUOT),
"ESTALE": uint(unix.ESTALE),
"EREMOTE": uint(unix.EREMOTE),
"ENOLCK": uint(unix.ENOLCK),
"ENOSYS": uint(unix.ENOSYS),
"EILSEQ": uint(unix.EILSEQ),
"ENOMEDIUM": uint(unix.ENOMEDIUM),
"EMEDIUMTYPE": uint(unix.EMEDIUMTYPE),
"EOVERFLOW": uint(unix.EOVERFLOW),
"ECANCELED": uint(unix.ECANCELED),
"EIDRM": uint(unix.EIDRM),
"ENOMSG": uint(unix.ENOMSG),
"ENOTSUP": uint(unix.ENOTSUP),
"EBADMSG": uint(unix.EBADMSG),
"ENOTRECOVERABLE": uint(unix.ENOTRECOVERABLE),
"EOWNERDEAD": uint(unix.EOWNERDEAD),
}

View File

@ -0,0 +1,240 @@
//go:build seccomp
// NOTE: this package has originally been copied from
// github.com/opencontainers/runc and modified to work for other use cases
package seccomp
import (
"errors"
"fmt"
specs "github.com/opencontainers/runtime-spec/specs-go"
libseccomp "github.com/seccomp/libseccomp-golang"
"golang.org/x/sys/unix"
)
// NOTE: this package has originally been copied from
// github.com/opencontainers/runc and modified to work for other use cases
var (
// ErrSpecNil is a possible return error from BuildFilter() and occurs if
// the provided spec is nil.
ErrSpecNil = errors.New("spec is nil")
// ErrSpecEmpty is a possible return error from BuildFilter() and occurs if
// the provided spec has neither a DefaultAction nor any syscalls.
ErrSpecEmpty = errors.New("spec contains neither a default action nor any syscalls")
)
// BuildFilter does a basic validation for the provided seccomp profile
// string and returns a filter for it.
func BuildFilter(spec *specs.LinuxSeccomp) (*libseccomp.ScmpFilter, error) {
// Sanity checking to allow consumers to act accordingly
if spec == nil {
return nil, ErrSpecNil
}
if spec.DefaultAction == "" && len(spec.Syscalls) == 0 {
return nil, ErrSpecEmpty
}
profile, err := specToSeccomp(spec)
if err != nil {
return nil, fmt.Errorf("convert spec to seccomp profile: %w", err)
}
defaultAction, err := toAction(profile.DefaultAction, profile.DefaultErrnoRet)
if err != nil {
return nil, fmt.Errorf("convert default action %s: %w", profile.DefaultAction, err)
}
filter, err := libseccomp.NewFilter(defaultAction)
if err != nil {
return nil, fmt.Errorf("create filter for default action %s: %w", defaultAction, err)
}
// Add extra architectures
for _, arch := range spec.Architectures {
libseccompArch, err := specArchToLibseccompArch(arch)
if err != nil {
return nil, fmt.Errorf("convert spec arch: %w", err)
}
scmpArch, err := libseccomp.GetArchFromString(libseccompArch)
if err != nil {
return nil, fmt.Errorf("validate Seccomp architecture %s: %w", arch, err)
}
if err := filter.AddArch(scmpArch); err != nil {
return nil, fmt.Errorf("add architecture to seccomp filter: %w", err)
}
}
// Unset no new privs bit
if err := filter.SetNoNewPrivsBit(false); err != nil {
return nil, fmt.Errorf("set no new privileges flag: %w", err)
}
// Add a rule for each syscall
for _, call := range profile.Syscalls {
if call == nil {
return nil, errors.New("encountered nil syscall while initializing seccomp")
}
if err = matchSyscall(filter, call); err != nil {
return nil, fmt.Errorf("filter matches syscall: %w", err)
}
}
return filter, nil
}
func matchSyscall(filter *libseccomp.ScmpFilter, call *Syscall) error {
if call == nil || filter == nil {
return errors.New("cannot use nil as syscall to block")
}
if call.Name == "" {
return errors.New("empty string is not a valid syscall")
}
// If we can't resolve the syscall, assume it's not supported on this kernel
// Ignore it, don't error out
callNum, err := libseccomp.GetSyscallFromName(call.Name)
if err != nil {
return nil
}
// Convert the call's action to the libseccomp equivalent
callAct, err := toAction(call.Action, call.ErrnoRet)
if err != nil {
return fmt.Errorf("convert action %s: %w", call.Action, err)
}
// Unconditional match - just add the rule
if len(call.Args) == 0 {
if err = filter.AddRule(callNum, callAct); err != nil {
return fmt.Errorf("add seccomp filter rule for syscall %s: %w", call.Name, err)
}
} else {
// Linux system calls can have at most 6 arguments
const syscallMaxArguments int = 6
// If two or more arguments have the same condition,
// Revert to old behavior, adding each condition as a separate rule
argCounts := make([]uint, syscallMaxArguments)
conditions := []libseccomp.ScmpCondition{}
for _, cond := range call.Args {
newCond, err := toCondition(cond)
if err != nil {
return fmt.Errorf("create seccomp syscall condition for syscall %s: %w", call.Name, err)
}
argCounts[cond.Index]++
conditions = append(conditions, newCond)
}
hasMultipleArgs := false
for _, count := range argCounts {
if count > 1 {
hasMultipleArgs = true
break
}
}
if hasMultipleArgs {
// Revert to old behavior
// Add each condition attached to a separate rule
for _, cond := range conditions {
condArr := []libseccomp.ScmpCondition{cond}
if err = filter.AddRuleConditional(callNum, callAct, condArr); err != nil {
return fmt.Errorf("add seccomp rule for syscall %s: %w", call.Name, err)
}
}
} else if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil {
// No conditions share same argument
// Use new, proper behavior
return fmt.Errorf("add seccomp rule for syscall %s: %w", call.Name, err)
}
}
return nil
}
// toAction converts an internal `Action` type to a `libseccomp.ScmpAction`
// type.
func toAction(act Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
switch act {
case ActKill:
// lint was not passing until this was changed from ActKill to ActKilThread.
return libseccomp.ActKillThread, nil
case ActKillProcess:
return libseccomp.ActKillProcess, nil
case ActErrno:
if errnoRet != nil {
return libseccomp.ActErrno.SetReturnCode(int16(*errnoRet)), nil
}
return libseccomp.ActErrno.SetReturnCode(int16(unix.EPERM)), nil
case ActTrap:
return libseccomp.ActTrap, nil
case ActAllow:
return libseccomp.ActAllow, nil
case ActTrace:
if errnoRet != nil {
return libseccomp.ActTrace.SetReturnCode(int16(*errnoRet)), nil
}
return libseccomp.ActTrace.SetReturnCode(int16(unix.EPERM)), nil
case ActLog:
return libseccomp.ActLog, nil
default:
return libseccomp.ActInvalid, fmt.Errorf("invalid action %s", act)
}
}
// toCondition converts an internal `Arg` type to a `libseccomp.ScmpCondition`
// type.
func toCondition(arg *Arg) (cond libseccomp.ScmpCondition, err error) {
if arg == nil {
return cond, errors.New("cannot convert nil to syscall condition")
}
op, err := toCompareOp(arg.Op)
if err != nil {
return cond, fmt.Errorf("convert compare operator: %w", err)
}
condition, err := libseccomp.MakeCondition(
arg.Index, op, arg.Value, arg.ValueTwo,
)
if err != nil {
return cond, fmt.Errorf("make condition: %w", err)
}
return condition, nil
}
// toCompareOp converts an internal `Operator` type to a
// `libseccomp.ScmpCompareOp`.
func toCompareOp(op Operator) (libseccomp.ScmpCompareOp, error) {
switch op {
case OpEqualTo:
return libseccomp.CompareEqual, nil
case OpNotEqual:
return libseccomp.CompareNotEqual, nil
case OpGreaterThan:
return libseccomp.CompareGreater, nil
case OpGreaterEqual:
return libseccomp.CompareGreaterEqual, nil
case OpLessThan:
return libseccomp.CompareLess, nil
case OpLessEqual:
return libseccomp.CompareLessOrEqual, nil
case OpMaskedEqual:
return libseccomp.CompareMaskedEqual, nil
default:
return libseccomp.CompareInvalid, fmt.Errorf("invalid operator %s", op)
}
}

View File

@ -1,5 +1,7 @@
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 38,
"defaultErrno": "ENOSYS",
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
@ -50,6 +52,52 @@
}
],
"syscalls": [
{
"names": [
"bdflush",
"cachestat",
"futex_requeue",
"futex_wait",
"futex_waitv",
"futex_wake",
"io_pgetevents",
"io_pgetevents_time64",
"kexec_file_load",
"kexec_load",
"map_shadow_stack",
"migrate_pages",
"move_pages",
"nfsservctl",
"nice",
"oldfstat",
"oldlstat",
"oldolduname",
"oldstat",
"olduname",
"pciconfig_iobase",
"pciconfig_read",
"pciconfig_write",
"sgetmask",
"ssetmask",
"swapoff",
"swapon",
"syscall",
"sysfs",
"uselib",
"userfaultfd",
"ustat",
"vm86",
"vm86old",
"vmsplice"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"_llseek",
@ -67,10 +115,18 @@
"chmod",
"chown",
"chown32",
"clock_adjtime",
"clock_adjtime64",
"clock_getres",
"clock_getres_time64",
"clock_gettime",
"clock_gettime64",
"clock_nanosleep",
"clock_nanosleep_time64",
"clone",
"clone3",
"close",
"close_range",
"connect",
"copy_file_range",
"creat",
@ -82,6 +138,7 @@
"epoll_ctl",
"epoll_ctl_old",
"epoll_pwait",
"epoll_pwait2",
"epoll_wait",
"epoll_wait_old",
"eventfd",
@ -91,6 +148,7 @@
"exit",
"exit_group",
"faccessat",
"faccessat2",
"fadvise64",
"fadvise64_64",
"fallocate",
@ -98,6 +156,7 @@
"fchdir",
"fchmod",
"fchmodat",
"fchmodat2",
"fchown",
"fchown32",
"fchownat",
@ -109,7 +168,11 @@
"flock",
"fork",
"fremovexattr",
"fsconfig",
"fsetxattr",
"fsmount",
"fsopen",
"fspick",
"fstat",
"fstat64",
"fstatat64",
@ -119,7 +182,9 @@
"ftruncate",
"ftruncate64",
"futex",
"futex_time64",
"futimesat",
"get_mempolicy",
"get_robust_list",
"get_thread_area",
"getcpu",
@ -169,7 +234,11 @@
"ioprio_get",
"ioprio_set",
"ipc",
"keyctl",
"kill",
"landlock_add_rule",
"landlock_create_ruleset",
"landlock_restrict_self",
"lchown",
"lchown32",
"lgetxattr",
@ -184,7 +253,10 @@
"lstat",
"lstat64",
"madvise",
"mbind",
"membarrier",
"memfd_create",
"memfd_secret",
"mincore",
"mkdir",
"mkdirat",
@ -196,12 +268,16 @@
"mmap",
"mmap2",
"mount",
"mount_setattr",
"move_mount",
"mprotect",
"mq_getsetattr",
"mq_notify",
"mq_open",
"mq_timedreceive",
"mq_timedreceive_time64",
"mq_timedsend",
"mq_timedsend_time64",
"mq_unlink",
"mremap",
"msgctl",
@ -216,18 +292,33 @@
"nanosleep",
"newfstatat",
"open",
"open_tree",
"openat",
"openat2",
"pause",
"pidfd_getfd",
"pidfd_open",
"pidfd_send_signal",
"pipe",
"pipe2",
"pivot_root",
"pkey_alloc",
"pkey_free",
"pkey_mprotect",
"poll",
"ppoll",
"ppoll_time64",
"prctl",
"pread64",
"preadv",
"preadv2",
"prlimit64",
"process_mrelease",
"process_vm_readv",
"process_vm_writev",
"pselect6",
"pselect6_time64",
"ptrace",
"pwrite64",
"pwritev",
"pwritev2",
@ -240,6 +331,7 @@
"recv",
"recvfrom",
"recvmmsg",
"recvmmsg_time64",
"recvmsg",
"remap_file_pages",
"removexattr",
@ -248,6 +340,7 @@
"renameat2",
"restart_syscall",
"rmdir",
"rseq",
"rt_sigaction",
"rt_sigpending",
"rt_sigprocmask",
@ -255,6 +348,7 @@
"rt_sigreturn",
"rt_sigsuspend",
"rt_sigtimedwait",
"rt_sigtimedwait_time64",
"rt_tgsigqueueinfo",
"sched_get_priority_max",
"sched_get_priority_min",
@ -263,6 +357,7 @@
"sched_getparam",
"sched_getscheduler",
"sched_rr_get_interval",
"sched_rr_get_interval_time64",
"sched_setaffinity",
"sched_setattr",
"sched_setparam",
@ -274,12 +369,14 @@
"semget",
"semop",
"semtimedop",
"semtimedop_time64",
"send",
"sendfile",
"sendfile64",
"sendmmsg",
"sendmsg",
"sendto",
"set_mempolicy",
"set_robust_list",
"set_thread_area",
"set_tid_address",
@ -292,6 +389,7 @@
"setgroups",
"setgroups32",
"setitimer",
"setns",
"setpgid",
"setpriority",
"setregid",
@ -314,8 +412,10 @@
"shmget",
"shutdown",
"sigaltstack",
"signal",
"signalfd",
"signalfd4",
"sigprocmask",
"sigreturn",
"socketcall",
"socketpair",
@ -339,10 +439,14 @@
"timer_delete",
"timer_getoverrun",
"timer_gettime",
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
"timerfd_settime",
"timerfd_settime64",
"times",
"tkill",
"truncate",
@ -357,9 +461,9 @@
"unshare",
"utime",
"utimensat",
"utimensat_time64",
"utimes",
"vfork",
"vmsplice",
"wait4",
"waitid",
"waitpid",
@ -459,7 +563,8 @@
},
{
"names": [
"sync_file_range2"
"sync_file_range2",
"swapcontext"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@ -475,10 +580,10 @@
"names": [
"arm_fadvise64_64",
"arm_sync_file_range",
"sync_file_range2",
"breakpoint",
"cacheflush",
"set_tls"
"set_tls",
"sync_file_range2"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@ -539,6 +644,20 @@
},
"excludes": {}
},
{
"names": [
"riscv_flush_icache"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"arches": [
"riscv64"
]
},
"excludes": {}
},
{
"names": [
"open_by_handle_at"
@ -553,22 +672,32 @@
},
"excludes": {}
},
{
"names": [
"open_by_handle_at"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_DAC_READ_SEARCH"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"bpf",
"clone",
"fanotify_init",
"lookup_dcookie",
"mount",
"name_to_handle_at",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
"umount",
"umount2",
"unshare"
"setns"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@ -582,68 +711,26 @@
},
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 2080505856,
"valueTwo": 0,
"op": "SCMP_CMP_MASKED_EQ"
}
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
],
"arches": [
"s390",
"s390x"
]
}
},
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 1,
"value": 2080505856,
"valueTwo": 0,
"op": "SCMP_CMP_MASKED_EQ"
}
],
"comment": "s390 parameter ordering for clone is different",
"includes": {
"arches": [
"s390",
"s390x"
]
},
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"reboot"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"caps": [
"CAP_SYS_BOOT"
]
},
"excludes": {}
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -659,11 +746,27 @@
},
"excludes": {}
},
{
"names": [
"chroot"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_CHROOT"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"delete_module",
"init_module",
"finit_module",
"init_module",
"query_module"
],
"action": "SCMP_ACT_ALLOW",
@ -678,20 +781,22 @@
},
{
"names": [
"get_mempolicy",
"mbind",
"name_to_handle_at",
"set_mempolicy"
"delete_module",
"finit_module",
"init_module",
"query_module"
],
"action": "SCMP_ACT_ALLOW",
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_NICE"
"CAP_SYS_MODULE"
]
},
"excludes": {}
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
@ -707,12 +812,26 @@
},
"excludes": {}
},
{
"names": [
"acct"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_PACCT"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"kcmp",
"process_vm_readv",
"process_vm_writev",
"ptrace"
"process_madvise"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@ -726,8 +845,25 @@
},
{
"names": [
"iopl",
"ioperm"
"kcmp",
"process_madvise"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_PTRACE"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"ioperm",
"iopl"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@ -741,9 +877,27 @@
},
{
"names": [
"ioperm",
"iopl"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_RAWIO"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"clock_settime",
"clock_settime64",
"settimeofday",
"stime",
"clock_settime"
"stime"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@ -755,6 +909,25 @@
},
"excludes": {}
},
{
"names": [
"clock_settime",
"clock_settime64",
"settimeofday",
"stime"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_TIME"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"vhangup"
@ -769,6 +942,22 @@
},
"excludes": {}
},
{
"names": [
"vhangup"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_TTY_CONFIG"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"socket"
@ -795,7 +984,8 @@
"CAP_AUDIT_WRITE"
]
},
"errnoRet": 22
"errnoRet": 22,
"errno": "EINVAL"
},
{
"names": [
@ -873,6 +1063,68 @@
]
},
"excludes": {}
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_ADMIN",
"CAP_BPF"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"caps": [
"CAP_BPF"
]
},
"excludes": {}
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_ADMIN",
"CAP_BPF"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"caps": [
"CAP_PERFMON"
]
},
"excludes": {}
}
]
}

View File

@ -1,19 +1,19 @@
// +build seccomp
//go:build seccomp
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2018 Docker, Inc.
package seccomp // import "github.com/seccomp/containers-golang"
package seccomp
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"github.com/opencontainers/runtime-spec/specs-go"
libseccomp "github.com/seccomp/libseccomp-golang"
"golang.org/x/sys/unix"
)
//go:generate go run -tags 'seccomp' generate.go
@ -67,6 +67,37 @@ func inSlice(slice []string, s string) bool {
return false
}
func getArchitectures(config *Seccomp, newConfig *specs.LinuxSeccomp) error {
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
return errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
}
// if config.Architectures == 0 then libseccomp will figure out the architecture to use
if len(config.Architectures) != 0 {
for _, a := range config.Architectures {
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a))
}
}
return nil
}
func getErrno(errno string, def *uint) (*uint, error) {
if errno == "" {
return def, nil
}
v, err := strconv.ParseUint(errno, 10, 32)
if err == nil {
v2 := uint(v)
return &v2, nil
}
v2, found := errnoArch[errno]
if !found {
return nil, fmt.Errorf("unknown errno %s", errno)
}
return &v2, nil
}
func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
if config == nil {
return nil, nil
@ -80,22 +111,22 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
newConfig := &specs.LinuxSeccomp{}
var arch string
var native, err = libseccomp.GetNativeArch()
native, err := libseccomp.GetNativeArch()
if err == nil {
arch = native.String()
}
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
return nil, errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
if err := getArchitectures(config, newConfig); err != nil {
return nil, err
}
// if config.Architectures == 0 then libseccomp will figure out the architecture to use
if len(config.Architectures) != 0 {
for _, a := range config.Architectures {
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a))
}
for _, flag := range config.Flags {
newConfig.Flags = append(newConfig.Flags, specs.LinuxSeccompFlag(flag))
}
newConfig.ListenerPath = config.ListenerPath
newConfig.ListenerMetadata = config.ListenerMetadata
if len(config.ArchMap) != 0 {
for _, a := range config.ArchMap {
seccompArch, ok := nativeToSeccomp[arch]
@ -113,6 +144,11 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
newConfig.DefaultAction = specs.LinuxSeccompAction(config.DefaultAction)
newConfig.DefaultErrnoRet, err = getErrno(config.DefaultErrno, config.DefaultErrnoRet)
if err != nil {
return nil, err
}
Loop:
// Loop through all syscall blocks and convert them to libcontainer format after filtering them
for _, call := range config.Syscalls {
@ -123,7 +159,7 @@ Loop:
}
if len(call.Excludes.Caps) > 0 {
for _, c := range call.Excludes.Caps {
if inSlice(rs.Process.Capabilities.Bounding, c) {
if rs != nil && rs.Process != nil && rs.Process.Capabilities != nil && inSlice(rs.Process.Capabilities.Bounding, c) {
continue Loop
}
}
@ -135,7 +171,7 @@ Loop:
}
if len(call.Includes.Caps) > 0 {
for _, c := range call.Includes.Caps {
if !inSlice(rs.Process.Capabilities.Bounding, c) {
if rs != nil && rs.Process != nil && rs.Process.Capabilities != nil && !inSlice(rs.Process.Capabilities.Bounding, c) {
continue Loop
}
}
@ -145,12 +181,17 @@ Loop:
return nil, errors.New("'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'")
}
errno, err := getErrno(call.Errno, call.ErrnoRet)
if err != nil {
return nil, err
}
if call.Name != "" {
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args, call.ErrnoRet))
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args, errno))
}
if len(call.Names) > 0 {
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, call.ErrnoRet))
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, errno))
}
}
@ -180,12 +221,5 @@ func createSpecsSyscall(names []string, action Action, args []*Arg, errnoRet *ui
// IsEnabled returns true if seccomp is enabled for the host.
func IsEnabled() bool {
// Check if Seccomp is supported, via CONFIG_SECCOMP.
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
return true
}
}
return false
return IsSupported()
}

View File

@ -1,10 +1,10 @@
// +build !seccomp
//go:build !linux || !seccomp
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2018 Docker, Inc.
package seccomp // import "github.com/seccomp/containers-golang"
package seccomp
import (
"errors"
@ -14,17 +14,12 @@ import (
var errNotSupported = errors.New("seccomp not enabled in this build")
// DefaultProfile returns a nil pointer on unsupported systems.
func DefaultProfile() *Seccomp {
return nil
}
// LoadProfile returns an error on unsuppored systems
// LoadProfile returns an error on unsupported systems
func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
return nil, errNotSupported
}
// GetDefaultProfile returns an error on unsuppored systems
// GetDefaultProfile returns an error on unsupported systems
func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) {
return nil, errNotSupported
}
@ -43,3 +38,9 @@ func LoadProfileFromConfig(config *Seccomp, specgen *specs.Spec) (*specs.LinuxSe
func IsEnabled() bool {
return false
}
// IsSupported returns true if the system has been configured to support
// seccomp.
func IsSupported() bool {
return false
}

View File

@ -0,0 +1,49 @@
//go:build linux && seccomp
package seccomp
import (
"sync"
"golang.org/x/sys/unix"
)
var (
supported bool
supOnce sync.Once
)
// IsSupported returns true if the system has been configured to support
// seccomp (including the check for CONFIG_SECCOMP_FILTER kernel option).
func IsSupported() bool {
// Excerpts from prctl(2), section ERRORS:
//
// EACCES
// option is PR_SET_SECCOMP and arg2 is SECCOMP_MODE_FILTER, but
// the process does not have the CAP_SYS_ADMIN capability or has
// not set the no_new_privs attribute <...>.
// <...>
// EFAULT
// option is PR_SET_SECCOMP, arg2 is SECCOMP_MODE_FILTER, the
// system was built with CONFIG_SECCOMP_FILTER, and arg3 is an
// invalid address.
// <...>
// EINVAL
// option is PR_SET_SECCOMP or PR_GET_SECCOMP, and the kernel
// was not configured with CONFIG_SECCOMP.
//
// EINVAL
// option is PR_SET_SECCOMP, arg2 is SECCOMP_MODE_FILTER,
// and the kernel was not configured with CONFIG_SECCOMP_FILTER.
// <end of quote>
//
// Meaning, in case these kernel options are set (this is what we check
// for here), we will get some other error (most probably EACCES or
// EFAULT). IOW, EINVAL means "seccomp not supported", any other error
// means it is supported.
supOnce.Do(func() {
supported = unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0) != unix.EINVAL
})
return supported
}

View File

@ -1,4 +1,4 @@
package seccomp // import "github.com/seccomp/containers-golang"
package seccomp
// SPDX-License-Identifier: Apache-2.0
@ -7,11 +7,19 @@ package seccomp // import "github.com/seccomp/containers-golang"
// Seccomp represents the config for a seccomp profile for syscall restriction.
type Seccomp struct {
DefaultAction Action `json:"defaultAction"`
// DefaultErrnoRet is obsolete, please use DefaultErrno
DefaultErrnoRet *uint `json:"defaultErrnoRet,omitempty"`
DefaultErrno string `json:"defaultErrno,omitempty"`
// Architectures is kept to maintain backward compatibility with the old
// seccomp profile.
Architectures []Arch `json:"architectures,omitempty"`
ArchMap []Architecture `json:"archMap,omitempty"`
Syscalls []*Syscall `json:"syscalls"`
Architectures []Arch `json:"architectures,omitempty"`
ArchMap []Architecture `json:"archMap,omitempty"`
Syscalls []*Syscall `json:"syscalls"`
Flags []string `json:"flags,omitempty"`
ListenerPath string `json:"listenerPath,omitempty"`
ListenerMetadata string `json:"listenerMetadata,omitempty"`
}
// Architecture is used to represent a specific architecture
@ -27,6 +35,7 @@ type Arch string
// Additional architectures permitted to be used for system calls
// By default only the native architecture of the kernel is permitted
const (
ArchNative Arch = "SCMP_ARCH_NATIVE"
ArchX86 Arch = "SCMP_ARCH_X86"
ArchX86_64 Arch = "SCMP_ARCH_X86_64"
ArchX32 Arch = "SCMP_ARCH_X32"
@ -43,6 +52,9 @@ const (
ArchPPC64LE Arch = "SCMP_ARCH_PPC64LE"
ArchS390 Arch = "SCMP_ARCH_S390"
ArchS390X Arch = "SCMP_ARCH_S390X"
ArchPARISC Arch = "SCMP_ARCH_PARISC"
ArchPARISC64 Arch = "SCMP_ARCH_PARISC64"
ArchRISCV64 Arch = "SCMP_ARCH_RISCV64"
)
// Action taken upon Seccomp rule match
@ -50,11 +62,20 @@ type Action string
// Define actions for Seccomp rules
const (
ActKill Action = "SCMP_ACT_KILL"
ActTrap Action = "SCMP_ACT_TRAP"
ActErrno Action = "SCMP_ACT_ERRNO"
ActTrace Action = "SCMP_ACT_TRACE"
ActAllow Action = "SCMP_ACT_ALLOW"
// ActKill results in termination of the thread that made the system call.
ActKill Action = "SCMP_ACT_KILL"
// ActKillProcess results in termination of the entire process.
ActKillProcess Action = "SCMP_ACT_KILL_PROCESS"
// ActKillThread kills the thread that violated the rule. It is the same as
// ActKill. All other threads from the same thread group will continue to
// execute.
ActKillThread Action = "SCMP_ACT_KILL_THREAD"
ActTrap Action = "SCMP_ACT_TRAP"
ActErrno Action = "SCMP_ACT_ERRNO"
ActTrace Action = "SCMP_ACT_TRACE"
ActAllow Action = "SCMP_ACT_ALLOW"
ActLog Action = "SCMP_ACT_LOG"
ActNotify Action = "SCMP_ACT_NOTIFY"
)
// Operator used to match syscall arguments in Seccomp
@ -94,5 +115,7 @@ type Syscall struct {
Comment string `json:"comment"`
Includes Filter `json:"includes"`
Excludes Filter `json:"excludes"`
ErrnoRet *uint `json:"errnoRet,omitempty"`
// ErrnoRet is obsolete, please use Errno
ErrnoRet *uint `json:"errnoRet,omitempty"`
Errno string `json:"errno,omitempty"`
}

View File

@ -0,0 +1,28 @@
//go:build seccomp
package seccomp
import (
"encoding/json"
"fmt"
)
// ValidateProfile does a basic validation for the provided seccomp profile
// string.
func ValidateProfile(content string) error {
profile := &Seccomp{}
if err := json.Unmarshal([]byte(content), &profile); err != nil {
return fmt.Errorf("decoding seccomp profile: %w", err)
}
spec, err := setupSeccomp(profile, nil)
if err != nil {
return fmt.Errorf("create seccomp spec: %w", err)
}
if _, err := BuildFilter(spec); err != nil {
return fmt.Errorf("build seccomp filter: %w", err)
}
return nil
}

1523
vendor/github.com/containers/storage/AUTHORS generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@ -175,13 +176,13 @@
END OF TERMS AND CONDITIONS
Copyright 2018-2019 github.com/seccomp authors.
Copyright 2013-2016 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

19
vendor/github.com/containers/storage/NOTICE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Docker
Copyright 2012-2016 Docker, Inc.
This product includes software developed at Docker, Inc. (https://www.docker.com).
This product contains software (https://github.com/kr/pty) developed
by Keith Rarick, licensed under the MIT License.
The following is courtesy of our legal counsel:
Use and transfer of Docker may be subject to certain restrictions by the
United States and other governments.
It is your responsibility to ensure that your use and/or transfer does not
violate applicable laws.
For more information, please see https://www.bis.doc.gov
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.

View File

@ -0,0 +1,38 @@
package fileutils
import (
"errors"
"os"
"syscall"
"golang.org/x/sys/unix"
)
// Exists checks whether a file or directory exists at the given path.
// If the path is a symlink, the symlink is followed.
func Exists(path string) error {
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
// simply checking the existence of a file.
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, 0)
if err != nil {
return &os.PathError{Op: "faccessat", Path: path, Err: err}
}
return nil
}
// Lexists checks whether a file or directory exists at the given path.
// If the path is a symlink, the symlink itself is checked.
func Lexists(path string) error {
// FreeBSD before 15.0 does not support the AT_SYMLINK_NOFOLLOW flag for
// faccessat. In this case, the call to faccessat will return EINVAL and
// we fall back to using Lstat.
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
if errors.Is(err, syscall.EINVAL) {
_, err = os.Lstat(path)
return err
}
return &os.PathError{Op: "faccessat", Path: path, Err: err}
}
return nil
}

View File

@ -0,0 +1,33 @@
//go:build !windows && !freebsd
package fileutils
import (
"os"
"golang.org/x/sys/unix"
)
// Exists checks whether a file or directory exists at the given path.
// If the path is a symlink, the symlink is followed.
func Exists(path string) error {
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
// simply checking the existence of a file.
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_EACCESS)
if err != nil {
return &os.PathError{Op: "faccessat", Path: path, Err: err}
}
return nil
}
// Lexists checks whether a file or directory exists at the given path.
// If the path is a symlink, the symlink itself is checked.
func Lexists(path string) error {
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
// simply checking the existence of a file.
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW|unix.AT_EACCESS)
if err != nil {
return &os.PathError{Op: "faccessat", Path: path, Err: err}
}
return nil
}

View File

@ -0,0 +1,18 @@
package fileutils
import (
"os"
)
// Exists checks whether a file or directory exists at the given path.
func Exists(path string) error {
_, err := os.Stat(path)
return err
}
// Lexists checks whether a file or directory exists at the given path, without
// resolving symlinks
func Lexists(path string) error {
_, err := os.Lstat(path)
return err
}

View File

@ -0,0 +1,371 @@
package fileutils
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"text/scanner"
"github.com/sirupsen/logrus"
)
// PatternMatcher allows checking paths against a list of patterns
type PatternMatcher struct {
patterns []*Pattern
exclusions bool
}
// NewPatternMatcher creates a new matcher object for specific patterns that can
// be used later to match against patterns against paths
func NewPatternMatcher(patterns []string) (*PatternMatcher, error) {
pm := &PatternMatcher{
patterns: make([]*Pattern, 0, len(patterns)),
}
for _, p := range patterns {
// Eliminate leading and trailing whitespace.
p = strings.TrimSpace(p)
if p == "" {
continue
}
p = filepath.Clean(p)
newp := &Pattern{}
if p[0] == '!' {
if len(p) == 1 {
return nil, errors.New("illegal exclusion pattern: \"!\"")
}
newp.exclusion = true
p = strings.TrimPrefix(filepath.Clean(p[1:]), "/")
pm.exclusions = true
}
// Do some syntax checking on the pattern.
// filepath's Match() has some really weird rules that are inconsistent
// so instead of trying to dup their logic, just call Match() for its
// error state and if there is an error in the pattern return it.
// If this becomes an issue we can remove this since its really only
// needed in the error (syntax) case - which isn't really critical.
if _, err := filepath.Match(p, "."); err != nil {
return nil, err
}
newp.cleanedPattern = p
newp.dirs = strings.Split(p, string(os.PathSeparator))
pm.patterns = append(pm.patterns, newp)
}
return pm, nil
}
// Deprecated: Please use the `MatchesResult` method instead.
// Matches matches path against all the patterns. Matches is not safe to be
// called concurrently
func (pm *PatternMatcher) Matches(file string) (bool, error) {
matched := false
file = filepath.FromSlash(file)
for _, pattern := range pm.patterns {
negative := false
if pattern.exclusion {
negative = true
}
match, err := pattern.match(file)
if err != nil {
return false, err
}
if match {
matched = !negative
}
}
if matched {
logrus.Debugf("Skipping excluded path: %s", file)
}
return matched, nil
}
type MatchResult struct {
isMatched bool
matches, excludes uint
}
// Excludes returns true if the overall result is matched
func (m *MatchResult) IsMatched() bool {
return m.isMatched
}
// Excludes returns the amount of matches of an MatchResult
func (m *MatchResult) Matches() uint {
return m.matches
}
// Excludes returns the amount of excludes of an MatchResult
func (m *MatchResult) Excludes() uint {
return m.excludes
}
// MatchesResult verifies the provided filepath against all patterns.
// It returns the `*MatchResult` result for the patterns on success, otherwise
// an error. This method is not safe to be called concurrently.
func (pm *PatternMatcher) MatchesResult(file string) (res *MatchResult, err error) {
file = filepath.FromSlash(file)
res = &MatchResult{false, 0, 0}
for _, pattern := range pm.patterns {
negative := false
if pattern.exclusion {
negative = true
}
match, err := pattern.match(file)
if err != nil {
return nil, err
}
if match {
res.isMatched = !negative
if negative {
res.excludes++
} else {
res.matches++
}
}
}
if res.matches > 0 {
logrus.Debugf("Skipping excluded path: %s", file)
}
return res, nil
}
// IsMatch verifies the provided filepath against all patterns and returns true
// if it matches. A match is valid if the last match is a positive one.
// It returns an error on failure and is not safe to be called concurrently.
func (pm *PatternMatcher) IsMatch(file string) (matched bool, err error) {
res, err := pm.MatchesResult(file)
if err != nil {
return false, err
}
return res.isMatched, nil
}
// Exclusions returns true if any of the patterns define exclusions
func (pm *PatternMatcher) Exclusions() bool {
return pm.exclusions
}
// Patterns returns array of active patterns
func (pm *PatternMatcher) Patterns() []*Pattern {
return pm.patterns
}
// Pattern defines a single regexp used to filter file paths.
type Pattern struct {
cleanedPattern string
dirs []string
regexp *regexp.Regexp
exclusion bool
}
func (p *Pattern) String() string {
return p.cleanedPattern
}
// Exclusion returns true if this pattern defines exclusion
func (p *Pattern) Exclusion() bool {
return p.exclusion
}
func (p *Pattern) match(path string) (bool, error) {
if p.regexp == nil {
if err := p.compile(); err != nil {
return false, filepath.ErrBadPattern
}
}
b := p.regexp.MatchString(path)
return b, nil
}
func (p *Pattern) compile() error {
regStr := "^"
pattern := p.cleanedPattern
// Go through the pattern and convert it to a regexp.
// We use a scanner so we can support utf-8 chars.
var scan scanner.Scanner
scan.Init(strings.NewReader(pattern))
sl := string(os.PathSeparator)
escSL := sl
const bs = `\`
if sl == bs {
escSL += bs
}
for scan.Peek() != scanner.EOF {
ch := scan.Next()
if ch == '*' {
if scan.Peek() == '*' {
// is some flavor of "**"
scan.Next()
// Treat **/ as ** so eat the "/"
if string(scan.Peek()) == sl {
scan.Next()
}
if scan.Peek() == scanner.EOF {
// is "**EOF" - to align with .gitignore just accept all
regStr += ".*"
} else {
// is "**"
// Note that this allows for any # of /'s (even 0) because
// the .* will eat everything, even /'s
regStr += "(.*" + escSL + ")?"
}
} else {
// is "*" so map it to anything but "/"
regStr += "[^" + escSL + "]*"
}
} else if ch == '?' {
// "?" is any char except "/"
regStr += "[^" + escSL + "]"
} else if ch == '.' || ch == '$' {
// Escape some regexp special chars that have no meaning
// in golang's filepath.Match
regStr += bs + string(ch)
} else if ch == '\\' {
// escape next char.
if sl == bs {
// On windows map "\" to "\\", meaning an escaped backslash,
// and then just continue because filepath.Match on
// Windows doesn't allow escaping at all
regStr += escSL
continue
}
if scan.Peek() != scanner.EOF {
regStr += bs + string(scan.Next())
} else {
return filepath.ErrBadPattern
}
} else {
regStr += string(ch)
}
}
regStr += "(" + escSL + ".*)?$"
re, err := regexp.Compile(regStr)
if err != nil {
return err
}
p.regexp = re
return nil
}
// Matches returns true if file matches any of the patterns
// and isn't excluded by any of the subsequent patterns.
func Matches(file string, patterns []string) (bool, error) {
pm, err := NewPatternMatcher(patterns)
if err != nil {
return false, err
}
file = filepath.Clean(file)
if file == "." {
// Don't let them exclude everything, kind of silly.
return false, nil
}
return pm.IsMatch(file)
}
// CopyFile copies from src to dst until either EOF is reached
// on src or an error occurs. It verifies src exists and removes
// the dst if it exists.
func CopyFile(src, dst string) (int64, error) {
cleanSrc := filepath.Clean(src)
cleanDst := filepath.Clean(dst)
if cleanSrc == cleanDst {
return 0, nil
}
sf, err := os.Open(cleanSrc)
if err != nil {
return 0, err
}
defer sf.Close()
if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) {
return 0, err
}
df, err := os.Create(cleanDst)
if err != nil {
return 0, err
}
defer df.Close()
return io.Copy(df, sf)
}
// ReadSymlinkedDirectory returns the target directory of a symlink.
// The target of the symbolic link may not be a file.
func ReadSymlinkedDirectory(path string) (string, error) {
var realPath string
var err error
if realPath, err = filepath.Abs(path); err != nil {
return "", fmt.Errorf("unable to get absolute path for %s: %w", path, err)
}
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
return "", fmt.Errorf("failed to canonicalise path for %s: %w", path, err)
}
realPathInfo, err := os.Stat(realPath)
if err != nil {
return "", fmt.Errorf("failed to stat target '%s' of '%s': %w", realPath, path, err)
}
if !realPathInfo.Mode().IsDir() {
return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
}
return realPath, nil
}
// ReadSymlinkedPath returns the target directory of a symlink.
// The target of the symbolic link can be a file and a directory.
func ReadSymlinkedPath(path string) (realPath string, err error) {
if realPath, err = filepath.Abs(path); err != nil {
return "", fmt.Errorf("unable to get absolute path for %q: %w", path, err)
}
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
return "", fmt.Errorf("failed to canonicalise path for %q: %w", path, err)
}
if err := Exists(realPath); err != nil {
return "", fmt.Errorf("failed to stat target %q of %q: %w", realPath, path, err)
}
return realPath, nil
}
// CreateIfNotExists creates a file or a directory only if it does not already exist.
func CreateIfNotExists(path string, isDir bool) error {
if err := Exists(path); err != nil {
if os.IsNotExist(err) {
if isDir {
return os.MkdirAll(path, 0o755)
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
f, err := os.OpenFile(path, os.O_CREATE, 0o755)
if err != nil {
return err
}
f.Close()
}
}
return nil
}

View File

@ -0,0 +1,27 @@
package fileutils
import (
"os"
"os/exec"
"strconv"
"strings"
)
// GetTotalUsedFds returns the number of used File Descriptors by
// executing `lsof -p PID`
func GetTotalUsedFds() int {
pid := os.Getpid()
cmd := exec.Command("lsof", "-p", strconv.Itoa(pid))
output, err := cmd.CombinedOutput()
if err != nil {
return -1
}
outputStr := strings.TrimSpace(string(output))
fds := strings.Split(outputStr, "\n")
return len(fds) - 1
}

View File

@ -0,0 +1,7 @@
package fileutils
// GetTotalUsedFds Returns the number of used File Descriptors.
// On Solaris these limits are per process and not systemwide
func GetTotalUsedFds() int {
return -1
}

View File

@ -0,0 +1,21 @@
//go:build linux || freebsd
package fileutils
import (
"fmt"
"os"
"github.com/sirupsen/logrus"
)
// GetTotalUsedFds Returns the number of used File Descriptors by
// reading it via /proc filesystem.
func GetTotalUsedFds() int {
if fds, err := os.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
logrus.Errorf("%v", err)
} else {
return len(fds)
}
return -1
}

View File

@ -0,0 +1,7 @@
package fileutils
// GetTotalUsedFds Returns the number of used File Descriptors. Not supported
// on Windows.
func GetTotalUsedFds() int {
return -1
}

View File

@ -0,0 +1,20 @@
package fileutils
import (
"io"
"os"
"golang.org/x/sys/unix"
)
// ReflinkOrCopy attempts to reflink the source to the destination fd.
// If reflinking fails or is unsupported, it falls back to io.Copy().
func ReflinkOrCopy(src, dst *os.File) error {
err := unix.IoctlFileClone(int(dst.Fd()), int(src.Fd()))
if err == nil {
return nil
}
_, err = io.Copy(dst, src)
return err
}

View File

@ -0,0 +1,15 @@
//go:build !linux
package fileutils
import (
"io"
"os"
)
// ReflinkOrCopy attempts to reflink the source to the destination fd.
// If reflinking fails or is unsupported, it falls back to io.Copy().
func ReflinkOrCopy(src, dst *os.File) error {
_, err := io.Copy(dst, src)
return err
}

View File

@ -0,0 +1,620 @@
package idtools
import (
"bufio"
"errors"
"fmt"
"io/fs"
"os"
"os/user"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"syscall"
"github.com/containers/storage/pkg/system"
"github.com/sirupsen/logrus"
)
// IDMap contains a single entry for user namespace range remapping. An array
// of IDMap entries represents the structure that will be provided to the Linux
// kernel for creating a user namespace.
type IDMap struct {
ContainerID int `json:"container_id"`
HostID int `json:"host_id"`
Size int `json:"size"`
}
type subIDRange struct {
Start int
Length int
}
type ranges []subIDRange
func (e ranges) Len() int { return len(e) }
func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start }
const (
subuidFileName string = "/etc/subuid"
subgidFileName string = "/etc/subgid"
ContainersOverrideXattr = "user.containers.override_stat"
)
// MkdirAllAs creates a directory (include any along the path) and then modifies
// ownership to the requested uid/gid. If the directory already exists, this
// function will still change ownership to the requested uid/gid pair.
// Deprecated: Use MkdirAllAndChown
func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
return mkdirAs(path, mode, ownerUID, ownerGID, true, true)
}
// MkdirAs creates a directory and then modifies ownership to the requested uid/gid.
// If the directory already exists, this function still changes ownership
// Deprecated: Use MkdirAndChown with a IDPair
func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
return mkdirAs(path, mode, ownerUID, ownerGID, false, true)
}
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
// ownership to the requested uid/gid. If the directory already exists, this
// function will still change ownership to the requested uid/gid pair.
func MkdirAllAndChown(path string, mode os.FileMode, ids IDPair) error {
return mkdirAs(path, mode, ids.UID, ids.GID, true, true)
}
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
// If the directory already exists, this function still changes ownership
func MkdirAndChown(path string, mode os.FileMode, ids IDPair) error {
return mkdirAs(path, mode, ids.UID, ids.GID, false, true)
}
// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
// ownership ONLY of newly created directories to the requested uid/gid. If the
// directories along the path exist, no change of ownership will be performed
func MkdirAllAndChownNew(path string, mode os.FileMode, ids IDPair) error {
return mkdirAs(path, mode, ids.UID, ids.GID, true, false)
}
// GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
// If the maps are empty, then the root uid/gid will default to "real" 0/0
func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) {
var uid, gid int
var err error
if len(uidMap) == 1 && uidMap[0].Size == 1 {
uid = uidMap[0].HostID
} else {
uid, err = RawToHost(0, uidMap)
if err != nil {
return -1, -1, err
}
}
if len(gidMap) == 1 && gidMap[0].Size == 1 {
gid = gidMap[0].HostID
} else {
gid, err = RawToHost(0, gidMap)
if err != nil {
return -1, -1, err
}
}
return uid, gid, nil
}
// RawToContainer takes an id mapping, and uses it to translate a host ID to
// the remapped ID. If no map is provided, then the translation assumes a
// 1-to-1 mapping and returns the passed in id.
//
// If you wish to map a (uid,gid) combination you should use the corresponding
// IDMappings methods, which ensure that you are mapping the correct ID against
// the correct mapping.
func RawToContainer(hostID int, idMap []IDMap) (int, error) {
if idMap == nil {
return hostID, nil
}
for _, m := range idMap {
if (hostID >= m.HostID) && (hostID <= (m.HostID + m.Size - 1)) {
contID := m.ContainerID + (hostID - m.HostID)
return contID, nil
}
}
return -1, fmt.Errorf("host ID %d cannot be mapped to a container ID", hostID)
}
// RawToHost takes an id mapping and a remapped ID, and translates the ID to
// the mapped host ID. If no map is provided, then the translation assumes a
// 1-to-1 mapping and returns the passed in id.
//
// If you wish to map a (uid,gid) combination you should use the corresponding
// IDMappings methods, which ensure that you are mapping the correct ID against
// the correct mapping.
func RawToHost(contID int, idMap []IDMap) (int, error) {
if idMap == nil {
return contID, nil
}
for _, m := range idMap {
if (contID >= m.ContainerID) && (contID <= (m.ContainerID + m.Size - 1)) {
hostID := m.HostID + (contID - m.ContainerID)
return hostID, nil
}
}
return -1, fmt.Errorf("container ID %d cannot be mapped to a host ID", contID)
}
// IDPair is a UID and GID pair
type IDPair struct {
UID int
GID int
}
// IDMappings contains a mappings of UIDs and GIDs
type IDMappings struct {
uids []IDMap
gids []IDMap
}
// NewIDMappings takes a requested user and group name and
// using the data from /etc/sub{uid,gid} ranges, creates the
// proper uid and gid remapping ranges for that user/group pair
func NewIDMappings(username, groupname string) (*IDMappings, error) {
subuidRanges, err := readSubuid(username)
if err != nil {
return nil, err
}
subgidRanges, err := readSubgid(groupname)
if err != nil {
return nil, err
}
if len(subuidRanges) == 0 {
return nil, fmt.Errorf("no subuid ranges found for user %q in %s", username, subuidFileName)
}
if len(subgidRanges) == 0 {
return nil, fmt.Errorf("no subgid ranges found for group %q in %s", groupname, subgidFileName)
}
return &IDMappings{
uids: createIDMap(subuidRanges),
gids: createIDMap(subgidRanges),
}, nil
}
// NewIDMappingsFromMaps creates a new mapping from two slices
// Deprecated: this is a temporary shim while transitioning to IDMapping
func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IDMappings {
return &IDMappings{uids: uids, gids: gids}
}
// RootPair returns a uid and gid pair for the root user. The error is ignored
// because a root user always exists, and the defaults are correct when the uid
// and gid maps are empty.
func (i *IDMappings) RootPair() IDPair {
uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
return IDPair{UID: uid, GID: gid}
}
// ToHost returns the host UID and GID for the container uid, gid.
func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) {
var err error
var target IDPair
target.UID, err = RawToHost(pair.UID, i.uids)
if err != nil {
return target, err
}
target.GID, err = RawToHost(pair.GID, i.gids)
return target, err
}
var (
overflowUIDOnce sync.Once
overflowGIDOnce sync.Once
overflowUID int
overflowGID int
)
// getOverflowUID returns the UID mapped to the overflow user
func getOverflowUID() int {
overflowUIDOnce.Do(func() {
// 65534 is the value on older kernels where /proc/sys/kernel/overflowuid is not present
overflowUID = 65534
if content, err := os.ReadFile("/proc/sys/kernel/overflowuid"); err == nil {
if tmp, err := strconv.Atoi(string(content)); err == nil {
overflowUID = tmp
}
}
})
return overflowUID
}
// getOverflowGID returns the GID mapped to the overflow user
func getOverflowGID() int {
overflowGIDOnce.Do(func() {
// 65534 is the value on older kernels where /proc/sys/kernel/overflowgid is not present
overflowGID = 65534
if content, err := os.ReadFile("/proc/sys/kernel/overflowgid"); err == nil {
if tmp, err := strconv.Atoi(string(content)); err == nil {
overflowGID = tmp
}
}
})
return overflowGID
}
// ToHost returns the host UID and GID for the container uid, gid.
// Remapping is only performed if the ids aren't already the remapped root ids
// If the mapping is not possible because the target ID is not mapped into
// the namespace, then the overflow ID is used.
func (i *IDMappings) ToHostOverflow(pair IDPair) (IDPair, error) {
var err error
target := i.RootPair()
if pair.UID != target.UID {
target.UID, err = RawToHost(pair.UID, i.uids)
if err != nil {
target.UID = getOverflowUID()
logrus.Debugf("Failed to map UID %v to the target mapping, using the overflow ID %v", pair.UID, target.UID)
}
}
if pair.GID != target.GID {
target.GID, err = RawToHost(pair.GID, i.gids)
if err != nil {
target.GID = getOverflowGID()
logrus.Debugf("Failed to map GID %v to the target mapping, using the overflow ID %v", pair.GID, target.GID)
}
}
return target, nil
}
// ToContainer returns the container UID and GID for the host uid and gid
func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) {
uid, err := RawToContainer(pair.UID, i.uids)
if err != nil {
return -1, -1, err
}
gid, err := RawToContainer(pair.GID, i.gids)
return uid, gid, err
}
// Empty returns true if there are no id mappings
func (i *IDMappings) Empty() bool {
return len(i.uids) == 0 && len(i.gids) == 0
}
// UIDs return the UID mapping
// TODO: remove this once everything has been refactored to use pairs
func (i *IDMappings) UIDs() []IDMap {
return i.uids
}
// GIDs return the UID mapping
// TODO: remove this once everything has been refactored to use pairs
func (i *IDMappings) GIDs() []IDMap {
return i.gids
}
func createIDMap(subidRanges ranges) []IDMap {
idMap := []IDMap{}
// sort the ranges by lowest ID first
sort.Sort(subidRanges)
containerID := 0
for _, idrange := range subidRanges {
idMap = append(idMap, IDMap{
ContainerID: containerID,
HostID: idrange.Start,
Size: idrange.Length,
})
containerID = containerID + idrange.Length
}
return idMap
}
// parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid)
// and return all found ranges for a specified username. If the special value
// "ALL" is supplied for username, then all ranges in the file will be returned
func parseSubidFile(path, username string) (ranges, error) {
var (
rangeList ranges
uidstr string
)
if u, err := user.Lookup(username); err == nil {
uidstr = u.Uid
}
subidFile, err := os.Open(path)
if err != nil {
return rangeList, err
}
defer subidFile.Close()
s := bufio.NewScanner(subidFile)
for s.Scan() {
if err := s.Err(); err != nil {
return rangeList, err
}
text := strings.TrimSpace(s.Text())
if text == "" || strings.HasPrefix(text, "#") {
continue
}
parts := strings.Split(text, ":")
if len(parts) != 3 {
return rangeList, fmt.Errorf("cannot parse subuid/gid information: Format not correct for %s file", path)
}
if parts[0] == username || username == "ALL" || (parts[0] == uidstr && parts[0] != "") {
startid, err := strconv.Atoi(parts[1])
if err != nil {
return rangeList, fmt.Errorf("string to int conversion failed during subuid/gid parsing of %s: %w", path, err)
}
length, err := strconv.Atoi(parts[2])
if err != nil {
return rangeList, fmt.Errorf("string to int conversion failed during subuid/gid parsing of %s: %w", path, err)
}
rangeList = append(rangeList, subIDRange{startid, length})
}
}
return rangeList, nil
}
func checkChownErr(err error, name string, uid, gid int) error {
var e *os.PathError
if errors.As(err, &e) && e.Err == syscall.EINVAL {
return fmt.Errorf(`potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally and run "podman system migrate": %w`, uid, gid, name, err)
}
return err
}
// Stat contains file states that can be overridden with ContainersOverrideXattr.
type Stat struct {
IDs IDPair
Mode os.FileMode
Major int
Minor int
}
// FormatContainersOverrideXattr will format the given uid, gid, and mode into a string
// that can be used as the value for the ContainersOverrideXattr xattr.
func FormatContainersOverrideXattr(uid, gid, mode int) string {
return FormatContainersOverrideXattrDevice(uid, gid, fs.FileMode(mode), 0, 0)
}
// FormatContainersOverrideXattrDevice will format the given uid, gid, and mode into a string
// that can be used as the value for the ContainersOverrideXattr xattr. For devices, it also
// needs the major and minor numbers.
func FormatContainersOverrideXattrDevice(uid, gid int, mode fs.FileMode, major, minor int) string {
typ := ""
switch mode & os.ModeType {
case os.ModeDir:
typ = "dir"
case os.ModeSymlink:
typ = "symlink"
case os.ModeNamedPipe:
typ = "pipe"
case os.ModeSocket:
typ = "socket"
case os.ModeDevice:
typ = fmt.Sprintf("block-%d-%d", major, minor)
case os.ModeDevice | os.ModeCharDevice:
typ = fmt.Sprintf("char-%d-%d", major, minor)
default:
typ = "file"
}
unixMode := mode & os.ModePerm
if mode&os.ModeSetuid != 0 {
unixMode |= 0o4000
}
if mode&os.ModeSetgid != 0 {
unixMode |= 0o2000
}
if mode&os.ModeSticky != 0 {
unixMode |= 0o1000
}
return fmt.Sprintf("%d:%d:%04o:%s", uid, gid, unixMode, typ)
}
// GetContainersOverrideXattr will get and decode ContainersOverrideXattr.
func GetContainersOverrideXattr(path string) (Stat, error) {
xstat, err := system.Lgetxattr(path, ContainersOverrideXattr)
if err != nil {
return Stat{}, err
}
return parseOverrideXattr(xstat) // This will fail if (xstat, err) == (nil, nil), i.e. the xattr does not exist.
}
func parseOverrideXattr(xstat []byte) (Stat, error) {
var stat Stat
attrs := strings.Split(string(xstat), ":")
if len(attrs) < 3 {
return stat, fmt.Errorf("the number of parts in %s is less than 3",
ContainersOverrideXattr)
}
value, err := strconv.ParseUint(attrs[0], 10, 32)
if err != nil {
return stat, fmt.Errorf("failed to parse UID: %w", err)
}
stat.IDs.UID = int(value)
value, err = strconv.ParseUint(attrs[1], 10, 32)
if err != nil {
return stat, fmt.Errorf("failed to parse GID: %w", err)
}
stat.IDs.GID = int(value)
value, err = strconv.ParseUint(attrs[2], 8, 32)
if err != nil {
return stat, fmt.Errorf("failed to parse mode: %w", err)
}
stat.Mode = os.FileMode(value) & os.ModePerm
if value&0o1000 != 0 {
stat.Mode |= os.ModeSticky
}
if value&0o2000 != 0 {
stat.Mode |= os.ModeSetgid
}
if value&0o4000 != 0 {
stat.Mode |= os.ModeSetuid
}
if len(attrs) > 3 {
typ := attrs[3]
if strings.HasPrefix(typ, "file") {
} else if strings.HasPrefix(typ, "dir") {
stat.Mode |= os.ModeDir
} else if strings.HasPrefix(typ, "symlink") {
stat.Mode |= os.ModeSymlink
} else if strings.HasPrefix(typ, "pipe") {
stat.Mode |= os.ModeNamedPipe
} else if strings.HasPrefix(typ, "socket") {
stat.Mode |= os.ModeSocket
} else if strings.HasPrefix(typ, "block") {
stat.Mode |= os.ModeDevice
stat.Major, stat.Minor, err = parseDevice(typ)
if err != nil {
return stat, err
}
} else if strings.HasPrefix(typ, "char") {
stat.Mode |= os.ModeDevice | os.ModeCharDevice
stat.Major, stat.Minor, err = parseDevice(typ)
if err != nil {
return stat, err
}
} else {
return stat, fmt.Errorf("invalid file type %s", typ)
}
}
return stat, nil
}
func parseDevice(typ string) (int, int, error) {
parts := strings.Split(typ, "-")
// If there are more than 3 parts, just ignore them to be forward compatible
if len(parts) < 3 {
return 0, 0, fmt.Errorf("invalid device type %s", typ)
}
if parts[0] != "block" && parts[0] != "char" {
return 0, 0, fmt.Errorf("invalid device type %s", typ)
}
major, err := strconv.Atoi(parts[1])
if err != nil {
return 0, 0, fmt.Errorf("failed to parse major number: %w", err)
}
minor, err := strconv.Atoi(parts[2])
if err != nil {
return 0, 0, fmt.Errorf("failed to parse minor number: %w", err)
}
return major, minor, nil
}
// SetContainersOverrideXattr will encode and set ContainersOverrideXattr.
func SetContainersOverrideXattr(path string, stat Stat) error {
value := FormatContainersOverrideXattrDevice(stat.IDs.UID, stat.IDs.GID, stat.Mode, stat.Major, stat.Minor)
return system.Lsetxattr(path, ContainersOverrideXattr, []byte(value), 0)
}
func SafeChown(name string, uid, gid int) error {
if runtime.GOOS == "darwin" {
stat := Stat{
Mode: os.FileMode(0o0700),
}
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
if err == nil && xstat != nil {
stat, err = parseOverrideXattr(xstat)
if err != nil {
return err
}
} else {
st, err := os.Stat(name) // Ideally we would share this with system.Stat below, but then we would need to convert Mode.
if err != nil {
return err
}
stat.Mode = st.Mode()
}
stat.IDs = IDPair{UID: uid, GID: gid}
if err = SetContainersOverrideXattr(name, stat); err != nil {
return err
}
uid = os.Getuid()
gid = os.Getgid()
}
if stat, statErr := system.Stat(name); statErr == nil {
if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) {
return nil
}
}
return checkChownErr(os.Chown(name, uid, gid), name, uid, gid)
}
func SafeLchown(name string, uid, gid int) error {
if runtime.GOOS == "darwin" {
stat := Stat{
Mode: os.FileMode(0o0700),
}
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
if err == nil && xstat != nil {
stat, err = parseOverrideXattr(xstat)
if err != nil {
return err
}
} else {
st, err := os.Lstat(name) // Ideally we would share this with system.Stat below, but then we would need to convert Mode.
if err != nil {
return err
}
stat.Mode = st.Mode()
}
stat.IDs = IDPair{UID: uid, GID: gid}
if err = SetContainersOverrideXattr(name, stat); err != nil {
return err
}
uid = os.Getuid()
gid = os.Getgid()
}
if stat, statErr := system.Lstat(name); statErr == nil {
if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) {
return nil
}
}
return checkChownErr(os.Lchown(name, uid, gid), name, uid, gid)
}
type sortByHostID []IDMap
func (e sortByHostID) Len() int { return len(e) }
func (e sortByHostID) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
func (e sortByHostID) Less(i, j int) bool { return e[i].HostID < e[j].HostID }
type sortByContainerID []IDMap
func (e sortByContainerID) Len() int { return len(e) }
func (e sortByContainerID) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
func (e sortByContainerID) Less(i, j int) bool { return e[i].ContainerID < e[j].ContainerID }
// IsContiguous checks if the specified mapping is contiguous and doesn't
// have any hole.
func IsContiguous(mappings []IDMap) bool {
if len(mappings) < 2 {
return true
}
var mh sortByHostID = mappings[:]
sort.Sort(mh)
for i := 1; i < len(mh); i++ {
if mh[i].HostID != mh[i-1].HostID+mh[i-1].Size {
return false
}
}
var mc sortByContainerID = mappings[:]
sort.Sort(mc)
for i := 1; i < len(mc); i++ {
if mc[i].ContainerID != mc[i-1].ContainerID+mc[i-1].Size {
return false
}
}
return true
}

View File

@ -0,0 +1,91 @@
//go:build linux && cgo && libsubid
package idtools
import (
"errors"
"os/user"
"sync"
"unsafe"
)
/*
#cgo LDFLAGS: -l subid
#include <shadow/subid.h>
#include <stdlib.h>
#include <stdio.h>
struct subid_range get_range(struct subid_range *ranges, int i)
{
return ranges[i];
}
#if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 4)
# define subid_init libsubid_init
# define subid_get_uid_ranges get_subuid_ranges
# define subid_get_gid_ranges get_subgid_ranges
#endif
*/
import "C"
var onceInit sync.Once
func readSubid(username string, isUser bool) (ranges, error) {
var ret ranges
uidstr := ""
if username == "ALL" {
return nil, errors.New("username ALL not supported")
}
if u, err := user.Lookup(username); err == nil {
uidstr = u.Uid
}
onceInit.Do(func() {
C.subid_init(C.CString("storage"), C.stderr)
})
cUsername := C.CString(username)
defer C.free(unsafe.Pointer(cUsername))
cuidstr := C.CString(uidstr)
defer C.free(unsafe.Pointer(cuidstr))
var nRanges C.int
var cRanges *C.struct_subid_range
if isUser {
nRanges = C.subid_get_uid_ranges(cUsername, &cRanges)
if nRanges <= 0 {
nRanges = C.subid_get_uid_ranges(cuidstr, &cRanges)
}
} else {
nRanges = C.subid_get_gid_ranges(cUsername, &cRanges)
if nRanges <= 0 {
nRanges = C.subid_get_gid_ranges(cuidstr, &cRanges)
}
}
if nRanges < 0 {
return nil, errors.New("cannot read subids")
}
defer C.free(unsafe.Pointer(cRanges))
for i := 0; i < int(nRanges); i++ {
r := C.get_range(cRanges, C.int(i))
newRange := subIDRange{
Start: int(r.start),
Length: int(r.count),
}
ret = append(ret, newRange)
}
return ret, nil
}
func readSubuid(username string) (ranges, error) {
return readSubid(username, true)
}
func readSubgid(username string) (ranges, error) {
return readSubid(username, false)
}

View File

@ -0,0 +1,214 @@
//go:build !windows
package idtools
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
"github.com/containers/storage/pkg/fileutils"
"github.com/containers/storage/pkg/system"
"github.com/moby/sys/user"
)
var (
entOnce sync.Once
getentCmd string
)
func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error {
// make an array containing the original path asked for, plus (for mkAll == true)
// all path components leading up to the complete path that don't exist before we MkdirAll
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
// chown the full directory path if it exists
var paths []string
st, err := os.Stat(path)
if err != nil && os.IsNotExist(err) {
paths = []string{path}
} else if err == nil {
if !st.IsDir() {
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
}
if chownExisting {
// short-circuit--we were called with an existing directory and chown was requested
return SafeChown(path, ownerUID, ownerGID)
}
// nothing to do; directory exists and chown was NOT requested
return nil
}
if mkAll {
// walk back to "/" looking for directories which do not exist
// and add them to the paths array for chown after creation
dirPath := path
if !filepath.IsAbs(dirPath) {
return fmt.Errorf("path: %s should be absolute", dirPath)
}
for {
dirPath = filepath.Dir(dirPath)
if dirPath == "/" {
break
}
if err := fileutils.Exists(dirPath); err != nil && os.IsNotExist(err) {
paths = append(paths, dirPath)
}
}
if err := os.MkdirAll(path, mode); err != nil {
return err
}
} else {
if err := os.Mkdir(path, mode); err != nil && !os.IsExist(err) {
return err
}
}
// even if it existed, we will chown the requested path + any subpaths that
// didn't exist when we called MkdirAll
for _, pathComponent := range paths {
if err := SafeChown(pathComponent, ownerUID, ownerGID); err != nil {
return err
}
}
return nil
}
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
// if that uid, gid pair has access (execute bit) to the directory
func CanAccess(path string, pair IDPair) bool {
statInfo, err := system.Stat(path)
if err != nil {
return false
}
fileMode := os.FileMode(statInfo.Mode())
permBits := fileMode.Perm()
return accessible(statInfo.UID() == uint32(pair.UID),
statInfo.GID() == uint32(pair.GID), permBits)
}
func accessible(isOwner, isGroup bool, perms os.FileMode) bool {
if isOwner && (perms&0o100 == 0o100) {
return true
}
if isGroup && (perms&0o010 == 0o010) {
return true
}
if perms&0o001 == 0o001 {
return true
}
return false
}
// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username,
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
func LookupUser(username string) (user.User, error) {
// first try a local system files lookup using existing capabilities
usr, err := user.LookupUser(username)
if err == nil {
return usr, nil
}
// local files lookup failed; attempt to call `getent` to query configured passwd dbs
usr, err = getentUser(fmt.Sprintf("%s %s", "passwd", username))
if err != nil {
return user.User{}, err
}
return usr, nil
}
// LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid,
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
func LookupUID(uid int) (user.User, error) {
// first try a local system files lookup using existing capabilities
usr, err := user.LookupUid(uid)
if err == nil {
return usr, nil
}
// local files lookup failed; attempt to call `getent` to query configured passwd dbs
return getentUser(fmt.Sprintf("%s %d", "passwd", uid))
}
func getentUser(args string) (user.User, error) {
reader, err := callGetent(args)
if err != nil {
return user.User{}, err
}
users, err := user.ParsePasswd(reader)
if err != nil {
return user.User{}, err
}
if len(users) == 0 {
return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", strings.Split(args, " ")[1])
}
return users[0], nil
}
// LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name,
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
func LookupGroup(groupname string) (user.Group, error) {
// first try a local system files lookup using existing capabilities
group, err := user.LookupGroup(groupname)
if err == nil {
return group, nil
}
// local files lookup failed; attempt to call `getent` to query configured group dbs
return getentGroup(fmt.Sprintf("%s %s", "group", groupname))
}
// LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID,
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
func LookupGID(gid int) (user.Group, error) {
// first try a local system files lookup using existing capabilities
group, err := user.LookupGid(gid)
if err == nil {
return group, nil
}
// local files lookup failed; attempt to call `getent` to query configured group dbs
return getentGroup(fmt.Sprintf("%s %d", "group", gid))
}
func getentGroup(args string) (user.Group, error) {
reader, err := callGetent(args)
if err != nil {
return user.Group{}, err
}
groups, err := user.ParseGroup(reader)
if err != nil {
return user.Group{}, err
}
if len(groups) == 0 {
return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", strings.Split(args, " ")[1])
}
return groups[0], nil
}
func callGetent(args string) (io.Reader, error) {
entOnce.Do(func() { getentCmd, _ = resolveBinary("getent") })
// if no `getent` command on host, can't do anything else
if getentCmd == "" {
return nil, fmt.Errorf("")
}
out, err := execCmd(getentCmd, args)
if err != nil {
exitCode, errC := system.GetExitCode(err)
if errC != nil {
return nil, err
}
switch exitCode {
case 1:
return nil, fmt.Errorf("getent reported invalid parameters/database unknown")
case 2:
terms := strings.Split(args, " ")
return nil, fmt.Errorf("getent unable to find entry %q in %s database", terms[1], terms[0])
case 3:
return nil, fmt.Errorf("getent database doesn't support enumeration")
default:
return nil, err
}
}
return bytes.NewReader(out), nil
}

View File

@ -0,0 +1,11 @@
//go:build !linux || !libsubid || !cgo
package idtools
func readSubuid(username string) (ranges, error) {
return parseSubidFile(subuidFileName, username)
}
func readSubgid(username string) (ranges, error) {
return parseSubidFile(subgidFileName, username)
}

View File

@ -0,0 +1,23 @@
//go:build windows
package idtools
import (
"os"
)
// Platforms such as Windows do not support the UID/GID concept. So make this
// just a wrapper around system.MkdirAll.
func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error {
if err := os.MkdirAll(path, mode); err != nil {
return err
}
return nil
}
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
// if that uid, gid pair has access (execute bit) to the directory
// Windows does not require/support this function, so always return true
func CanAccess(path string, pair IDPair) bool {
return true
}

View File

@ -0,0 +1,59 @@
package idtools
import (
"fmt"
"math"
"math/bits"
"strconv"
"strings"
)
func parseTriple(spec []string) (container, host, size uint32, err error) {
cid, err := strconv.ParseUint(spec[0], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("parsing id map value %q: %w", spec[0], err)
}
hid, err := strconv.ParseUint(spec[1], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("parsing id map value %q: %w", spec[1], err)
}
sz, err := strconv.ParseUint(spec[2], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("parsing id map value %q: %w", spec[2], err)
}
return uint32(cid), uint32(hid), uint32(sz), nil
}
// ParseIDMap parses idmap triples from string.
func ParseIDMap(mapSpec []string, mapSetting string) (idmap []IDMap, err error) {
stdErr := fmt.Errorf("initializing ID mappings: %s setting is malformed expected [\"uint32:uint32:uint32\"]: %q", mapSetting, mapSpec)
for _, idMapSpec := range mapSpec {
if idMapSpec == "" {
continue
}
idSpec := strings.Split(idMapSpec, ":")
if len(idSpec)%3 != 0 {
return nil, stdErr
}
for i := range idSpec {
if i%3 != 0 {
continue
}
cid, hid, size, err := parseTriple(idSpec[i : i+3])
if err != nil {
return nil, stdErr
}
// Avoid possible integer overflow on 32bit builds
if bits.UintSize == 32 && (cid > math.MaxInt32 || hid > math.MaxInt32 || size > math.MaxInt32) {
return nil, stdErr
}
mapping := IDMap{
ContainerID: int(cid),
HostID: int(hid),
Size: int(size),
}
idmap = append(idmap, mapping)
}
}
return idmap, nil
}

View File

@ -0,0 +1,164 @@
package idtools
import (
"fmt"
"sort"
"strconv"
"strings"
"sync"
"github.com/containers/storage/pkg/regexp"
)
// add a user and/or group to Linux /etc/passwd, /etc/group using standard
// Linux distribution commands:
// adduser --system --shell /bin/false --disabled-login --disabled-password --no-create-home --group <username>
// useradd -r -s /bin/false <username>
var (
once sync.Once
userCommand string
cmdTemplates = map[string]string{
"adduser": "--system --shell /bin/false --no-create-home --disabled-login --disabled-password --group %s",
"useradd": "-r -s /bin/false %s",
"usermod": "-%s %d-%d %s",
}
idOutRegexp = regexp.Delayed(`uid=([0-9]+).*gid=([0-9]+)`)
// default length for a UID/GID subordinate range
defaultRangeLen = 65536
defaultRangeStart = 100000
userMod = "usermod"
)
// AddNamespaceRangesUser takes a username and uses the standard system
// utility to create a system user/group pair used to hold the
// /etc/sub{uid,gid} ranges which will be used for user namespace
// mapping ranges in containers.
func AddNamespaceRangesUser(name string) (int, int, error) {
if err := addUser(name); err != nil {
return -1, -1, fmt.Errorf("adding user %q: %w", name, err)
}
// Query the system for the created uid and gid pair
out, err := execCmd("id", name)
if err != nil {
return -1, -1, fmt.Errorf("trying to find uid/gid for new user %q: %w", name, err)
}
matches := idOutRegexp.FindStringSubmatch(strings.TrimSpace(string(out)))
if len(matches) != 3 {
return -1, -1, fmt.Errorf("can't find uid, gid from `id` output: %q", string(out))
}
uid, err := strconv.Atoi(matches[1])
if err != nil {
return -1, -1, fmt.Errorf("can't convert found uid (%s) to int: %w", matches[1], err)
}
gid, err := strconv.Atoi(matches[2])
if err != nil {
return -1, -1, fmt.Errorf("can't convert found gid (%s) to int: %w", matches[2], err)
}
// Now we need to create the subuid/subgid ranges for our new user/group (system users
// do not get auto-created ranges in subuid/subgid)
if err := createSubordinateRanges(name); err != nil {
return -1, -1, fmt.Errorf("couldn't create subordinate ID ranges: %w", err)
}
return uid, gid, nil
}
func addUser(userName string) error {
once.Do(func() {
// set up which commands are used for adding users/groups dependent on distro
if _, err := resolveBinary("adduser"); err == nil {
userCommand = "adduser"
} else if _, err := resolveBinary("useradd"); err == nil {
userCommand = "useradd"
}
})
if userCommand == "" {
return fmt.Errorf("cannot add user; no useradd/adduser binary found")
}
args := fmt.Sprintf(cmdTemplates[userCommand], userName)
out, err := execCmd(userCommand, args)
if err != nil {
return fmt.Errorf("failed to add user with error: %w; output: %q", err, string(out))
}
return nil
}
func createSubordinateRanges(name string) error {
// first, we should verify that ranges weren't automatically created
// by the distro tooling
ranges, err := readSubuid(name)
if err != nil {
return fmt.Errorf("while looking for subuid ranges for user %q: %w", name, err)
}
if len(ranges) == 0 {
// no UID ranges; let's create one
startID, err := findNextUIDRange()
if err != nil {
return fmt.Errorf("can't find available subuid range: %w", err)
}
out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "v", startID, startID+defaultRangeLen-1, name))
if err != nil {
return fmt.Errorf("unable to add subuid range to user: %q; output: %s, err: %w", name, out, err)
}
}
ranges, err = readSubgid(name)
if err != nil {
return fmt.Errorf("while looking for subgid ranges for user %q: %w", name, err)
}
if len(ranges) == 0 {
// no GID ranges; let's create one
startID, err := findNextGIDRange()
if err != nil {
return fmt.Errorf("can't find available subgid range: %w", err)
}
out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "w", startID, startID+defaultRangeLen-1, name))
if err != nil {
return fmt.Errorf("unable to add subgid range to user: %q; output: %s, err: %w", name, out, err)
}
}
return nil
}
func findNextUIDRange() (int, error) {
ranges, err := readSubuid("ALL")
if err != nil {
return -1, fmt.Errorf("couldn't parse all ranges in /etc/subuid file: %w", err)
}
sort.Sort(ranges)
return findNextRangeStart(ranges)
}
func findNextGIDRange() (int, error) {
ranges, err := readSubgid("ALL")
if err != nil {
return -1, fmt.Errorf("couldn't parse all ranges in /etc/subgid file: %w", err)
}
sort.Sort(ranges)
return findNextRangeStart(ranges)
}
func findNextRangeStart(rangeList ranges) (int, error) {
startID := defaultRangeStart
for _, arange := range rangeList {
if wouldOverlap(arange, startID) {
startID = arange.Start + arange.Length
}
}
return startID, nil
}
func wouldOverlap(arange subIDRange, ID int) bool {
low := ID
high := ID + defaultRangeLen
if (low >= arange.Start && low <= arange.Start+arange.Length) ||
(high <= arange.Start+arange.Length && high >= arange.Start) {
return true
}
return false
}

View File

@ -0,0 +1,12 @@
//go:build !linux
package idtools
import "fmt"
// AddNamespaceRangesUser takes a name and finds an unused uid, gid pair
// and calls the appropriate helper function to add the group and then
// the user to the group in /etc/group and /etc/passwd respectively.
func AddNamespaceRangesUser(name string) (int, int, error) {
return -1, -1, fmt.Errorf("no support for adding users or groups on this OS")
}

View File

@ -0,0 +1,32 @@
//go:build !windows
package idtools
import (
"fmt"
"os/exec"
"path/filepath"
"strings"
)
func resolveBinary(binname string) (string, error) {
binaryPath, err := exec.LookPath(binname)
if err != nil {
return "", err
}
resolvedPath, err := filepath.EvalSymlinks(binaryPath)
if err != nil {
return "", err
}
// only return no error if the final resolved binary basename
// matches what was searched for
if filepath.Base(resolvedPath) == binname {
return resolvedPath, nil
}
return "", fmt.Errorf("binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath)
}
func execCmd(cmd, args string) ([]byte, error) {
execCmd := exec.Command(cmd, strings.Split(args, " ")...)
return execCmd.CombinedOutput()
}

149
vendor/github.com/containers/storage/pkg/mount/flags.go generated vendored Normal file
View File

@ -0,0 +1,149 @@
package mount
import (
"fmt"
"strings"
)
var flags = map[string]struct {
clear bool
flag int
}{
"defaults": {false, 0},
"ro": {false, RDONLY},
"rw": {true, RDONLY},
"suid": {true, NOSUID},
"nosuid": {false, NOSUID},
"dev": {true, NODEV},
"nodev": {false, NODEV},
"exec": {true, NOEXEC},
"noexec": {false, NOEXEC},
"sync": {false, SYNCHRONOUS},
"async": {true, SYNCHRONOUS},
"dirsync": {false, DIRSYNC},
"remount": {false, REMOUNT},
"mand": {false, MANDLOCK},
"nomand": {true, MANDLOCK},
"atime": {true, NOATIME},
"noatime": {false, NOATIME},
"diratime": {true, NODIRATIME},
"nodiratime": {false, NODIRATIME},
"bind": {false, BIND},
"rbind": {false, RBIND},
"unbindable": {false, UNBINDABLE},
"runbindable": {false, RUNBINDABLE},
"private": {false, PRIVATE},
"rprivate": {false, RPRIVATE},
"shared": {false, SHARED},
"rshared": {false, RSHARED},
"slave": {false, SLAVE},
"rslave": {false, RSLAVE},
"relatime": {false, RELATIME},
"norelatime": {true, RELATIME},
"strictatime": {false, STRICTATIME},
"nostrictatime": {true, STRICTATIME},
}
var validFlags = map[string]bool{
"": true,
"size": true,
"mode": true,
"uid": true,
"gid": true,
"nr_inodes": true,
"nr_blocks": true,
"mpol": true,
}
var propagationFlags = map[string]bool{
"bind": true,
"rbind": true,
"unbindable": true,
"runbindable": true,
"private": true,
"rprivate": true,
"shared": true,
"rshared": true,
"slave": true,
"rslave": true,
}
// MergeTmpfsOptions merge mount options to make sure there is no duplicate.
func MergeTmpfsOptions(options []string) ([]string, error) {
// We use collisions maps to remove duplicates.
// For flag, the key is the flag value (the key for propagation flag is -1)
// For data=value, the key is the data
flagCollisions := map[int]bool{}
dataCollisions := map[string]bool{}
var newOptions []string
// We process in reverse order
for i := len(options) - 1; i >= 0; i-- {
option := options[i]
if option == "defaults" {
continue
}
if f, ok := flags[option]; ok && f.flag != 0 {
// There is only one propagation mode
key := f.flag
if propagationFlags[option] {
key = -1
}
// Check to see if there is collision for flag
if !flagCollisions[key] {
// We prepend the option and add to collision map
newOptions = append([]string{option}, newOptions...)
flagCollisions[key] = true
}
continue
}
opt, _, ok := strings.Cut(option, "=")
if !ok || !validFlags[opt] {
return nil, fmt.Errorf("invalid tmpfs option %q", opt)
}
if !dataCollisions[opt] {
// We prepend the option and add to collision map
newOptions = append([]string{option}, newOptions...)
dataCollisions[opt] = true
}
}
return newOptions, nil
}
// ParseOptions parses fstab type mount options into mount() flags
// and device specific data
func ParseOptions(options string) (int, string) {
var (
flag int
data []string
)
for _, o := range strings.Split(options, ",") {
// If the option does not exist in the flags table or the flag
// is not supported on the platform,
// then it is a data value for a specific fs type
if f, exists := flags[o]; exists && f.flag != 0 {
if f.clear {
flag &= ^f.flag
} else {
flag |= f.flag
}
} else {
data = append(data, o)
}
}
return flag, strings.Join(data, ",")
}
// ParseTmpfsOptions parse fstab type mount options into flags and data
func ParseTmpfsOptions(options string) (int, string, error) {
flags, data := ParseOptions(options)
for _, o := range strings.Split(data, ",") {
opt, _, _ := strings.Cut(o, "=")
if !validFlags[opt] {
return 0, "", fmt.Errorf("invalid tmpfs option %q", opt)
}
}
return flags, data, nil
}

View File

@ -0,0 +1,48 @@
package mount
import (
"golang.org/x/sys/unix"
)
const (
// RDONLY will mount the file system read-only.
RDONLY = unix.MNT_RDONLY
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
// take effect.
NOSUID = unix.MNT_NOSUID
// NOEXEC will not allow execution of any binaries on the mounted file system.
NOEXEC = unix.MNT_NOEXEC
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
SYNCHRONOUS = unix.MNT_SYNCHRONOUS
// REMOUNT will attempt to remount an already-mounted file system. This is
// commonly used to change the mount flags for a file system, especially to
// make a readonly file system writeable. It does not change device or mount
// point.
REMOUNT = unix.MNT_UPDATE
// NOATIME will not update the file access time when reading from a file.
NOATIME = unix.MNT_NOATIME
mntDetach = unix.MNT_FORCE
NODIRATIME = 0
NODEV = 0
DIRSYNC = 0
MANDLOCK = 0
BIND = 0
RBIND = 0
UNBINDABLE = 0
RUNBINDABLE = 0
PRIVATE = 0
RPRIVATE = 0
SLAVE = 0
RSLAVE = 0
SHARED = 0
RSHARED = 0
RELATIME = 0
STRICTATIME = 0
)

View File

@ -0,0 +1,87 @@
package mount
import (
"golang.org/x/sys/unix"
)
const (
// RDONLY will mount the file system read-only.
RDONLY = unix.MS_RDONLY
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
// take effect.
NOSUID = unix.MS_NOSUID
// NODEV will not interpret character or block special devices on the file
// system.
NODEV = unix.MS_NODEV
// NOEXEC will not allow execution of any binaries on the mounted file system.
NOEXEC = unix.MS_NOEXEC
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
SYNCHRONOUS = unix.MS_SYNCHRONOUS
// DIRSYNC will force all directory updates within the file system to be done
// synchronously. This affects the following system calls: create, link,
// unlink, symlink, mkdir, rmdir, mknod and rename.
DIRSYNC = unix.MS_DIRSYNC
// REMOUNT will attempt to remount an already-mounted file system. This is
// commonly used to change the mount flags for a file system, especially to
// make a readonly file system writeable. It does not change device or mount
// point.
REMOUNT = unix.MS_REMOUNT
// MANDLOCK will force mandatory locks on a filesystem.
MANDLOCK = unix.MS_MANDLOCK
// NOATIME will not update the file access time when reading from a file.
NOATIME = unix.MS_NOATIME
// NODIRATIME will not update the directory access time.
NODIRATIME = unix.MS_NODIRATIME
// BIND remounts a subtree somewhere else.
BIND = unix.MS_BIND
// RBIND remounts a subtree and all possible submounts somewhere else.
RBIND = unix.MS_BIND | unix.MS_REC
// UNBINDABLE creates a mount which cannot be cloned through a bind operation.
UNBINDABLE = unix.MS_UNBINDABLE
// RUNBINDABLE marks the entire mount tree as UNBINDABLE.
RUNBINDABLE = unix.MS_UNBINDABLE | unix.MS_REC
// PRIVATE creates a mount which carries no propagation abilities.
PRIVATE = unix.MS_PRIVATE
// RPRIVATE marks the entire mount tree as PRIVATE.
RPRIVATE = unix.MS_PRIVATE | unix.MS_REC
// SLAVE creates a mount which receives propagation from its master, but not
// vice versa.
SLAVE = unix.MS_SLAVE
// RSLAVE marks the entire mount tree as SLAVE.
RSLAVE = unix.MS_SLAVE | unix.MS_REC
// SHARED creates a mount which provides the ability to create mirrors of
// that mount such that mounts and unmounts within any of the mirrors
// propagate to the other mirrors.
SHARED = unix.MS_SHARED
// RSHARED marks the entire mount tree as SHARED.
RSHARED = unix.MS_SHARED | unix.MS_REC
// RELATIME updates inode access times relative to modify or change time.
RELATIME = unix.MS_RELATIME
// STRICTATIME allows to explicitly request full atime updates. This makes
// it possible for the kernel to default to relatime or noatime but still
// allow userspace to override it.
STRICTATIME = unix.MS_STRICTATIME
mntDetach = unix.MNT_DETACH
)

View File

@ -0,0 +1,31 @@
//go:build !linux && !freebsd
package mount
// These flags are unsupported.
const (
BIND = 0
DIRSYNC = 0
MANDLOCK = 0
NOATIME = 0
NODEV = 0
NODIRATIME = 0
NOEXEC = 0
NOSUID = 0
UNBINDABLE = 0
RUNBINDABLE = 0
PRIVATE = 0
RPRIVATE = 0
SHARED = 0
RSHARED = 0
SLAVE = 0
RSLAVE = 0
RBIND = 0
RELATIME = 0
RELATIVE = 0
REMOUNT = 0
STRICTATIME = 0
SYNCHRONOUS = 0
RDONLY = 0
mntDetach = 0
)

110
vendor/github.com/containers/storage/pkg/mount/mount.go generated vendored Normal file
View File

@ -0,0 +1,110 @@
package mount
import (
"sort"
"strconv"
"strings"
)
// mountError holds an error from a mount or unmount operation
type mountError struct {
op string
source, target string
flags uintptr
data string
err error
}
// Error returns a string representation of mountError
func (e *mountError) Error() string {
out := e.op + " "
if e.source != "" {
out += e.source + ":" + e.target
} else {
out += e.target
}
if e.flags != uintptr(0) {
out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
}
if e.data != "" {
out += ", data: " + e.data
}
out += ": " + e.err.Error()
return out
}
// Cause returns the underlying cause of the error
func (e *mountError) Cause() error {
return e.err
}
// Unwrap returns the underlying cause of the error
func (e *mountError) Unwrap() error {
return e.err
}
// Mount will mount filesystem according to the specified configuration, on the
// condition that the target path is *not* already mounted. Options must be
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
// flags.go for supported option flags.
func Mount(device, target, mType, options string) error {
flag, data := ParseOptions(options)
if flag&REMOUNT != REMOUNT {
if mounted, err := Mounted(target); err != nil || mounted {
return err
}
}
return mount(device, target, mType, uintptr(flag), data)
}
// ForceMount will mount a filesystem according to the specified configuration,
// *regardless* if the target path is not already mounted. Options must be
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
// flags.go for supported option flags.
func ForceMount(device, target, mType, options string) error {
flag, data := ParseOptions(options)
return mount(device, target, mType, uintptr(flag), data)
}
// Unmount lazily unmounts a filesystem on supported platforms, otherwise
// does a normal unmount.
func Unmount(target string) error {
return unmount(target, mntDetach)
}
// RecursiveUnmount unmounts the target and all mounts underneath, starting with
// the deepest mount first.
func RecursiveUnmount(target string) error {
mounts, err := GetMounts()
if err != nil {
return err
}
// Make the deepest mount be first
sort.Slice(mounts, func(i, j int) bool {
return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint)
})
for i, m := range mounts {
if !strings.HasPrefix(m.Mountpoint, target) {
continue
}
if err := Unmount(m.Mountpoint); err != nil && i == len(mounts)-1 {
return err
// Ignore errors for submounts and continue trying to unmount others
// The final unmount should fail if there are any submounts remaining
}
}
return nil
}
// ForceUnmount lazily unmounts a filesystem on supported platforms,
// otherwise does a normal unmount.
//
// Deprecated: please use Unmount instead, it is identical.
func ForceUnmount(target string) error {
return unmount(target, mntDetach)
}

View File

@ -0,0 +1,63 @@
//go:build freebsd && cgo
package mount
/*
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/_iovec.h>
#include <sys/mount.h>
#include <sys/param.h>
*/
import "C"
import (
"fmt"
"strings"
"unsafe"
)
func allocateIOVecs(options []string) []C.struct_iovec {
out := make([]C.struct_iovec, len(options))
for i, option := range options {
out[i].iov_base = unsafe.Pointer(C.CString(option))
out[i].iov_len = C.size_t(len(option) + 1)
}
return out
}
func mount(device, target, mType string, flag uintptr, data string) error {
isNullFS := false
options := []string{"fspath", target}
if data != "" {
xs := strings.Split(data, ",")
for _, x := range xs {
if x == "bind" {
isNullFS = true
continue
}
name, val, _ := strings.Cut(x, "=")
options = append(options, name)
options = append(options, val)
}
}
if isNullFS {
options = append(options, "fstype", "nullfs", "target", device)
} else {
options = append(options, "fstype", mType, "from", device)
}
rawOptions := allocateIOVecs(options)
for _, rawOption := range rawOptions {
defer C.free(rawOption.iov_base)
}
if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 {
reason := C.GoString(C.strerror(*C.__error()))
return fmt.Errorf("failed to call nmount: %s", reason)
}
return nil
}

View File

@ -0,0 +1,74 @@
package mount
import (
"golang.org/x/sys/unix"
)
const (
// ptypes is the set propagation types.
ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE
// pflags is the full set valid flags for a change propagation call.
pflags = ptypes | unix.MS_REC | unix.MS_SILENT
// broflags is the combination of bind and read only
broflags = unix.MS_BIND | unix.MS_RDONLY
none = "none"
)
// isremount returns true if either device name or flags identify a remount request, false otherwise.
func isremount(device string, flags uintptr) bool {
switch {
// We treat device "" and "none" as a remount request to provide compatibility with
// requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts.
case flags&unix.MS_REMOUNT != 0, device == "", device == none:
return true
default:
return false
}
}
func mount(device, target, mType string, flags uintptr, data string) error {
oflags := flags &^ ptypes
if !isremount(device, flags) || data != "" {
// Initial call applying all non-propagation flags for mount
// or remount with changed data
if err := unix.Mount(device, target, mType, oflags, data); err != nil {
return &mountError{
op: "mount",
source: device,
target: target,
flags: oflags,
data: data,
err: err,
}
}
}
if flags&ptypes != 0 {
// Change the propagation type.
if err := unix.Mount("", target, "", flags&pflags, ""); err != nil {
return &mountError{
op: "remount",
target: target,
flags: flags & pflags,
err: err,
}
}
}
if oflags&broflags == broflags {
// Remount the bind to apply read only.
if err := unix.Mount("", target, "", oflags|unix.MS_REMOUNT, ""); err != nil {
return &mountError{
op: "remount-ro",
target: target,
flags: oflags | unix.MS_REMOUNT,
err: err,
}
}
}
return nil
}

View File

@ -0,0 +1,7 @@
//go:build !linux && !(freebsd && cgo)
package mount
func mount(device, target, mType string, flag uintptr, data string) error {
panic("Not implemented")
}

View File

@ -0,0 +1,13 @@
package mount
import (
"github.com/moby/sys/mountinfo"
)
type Info = mountinfo.Info
var Mounted = mountinfo.Mounted
func GetMounts() ([]*Info, error) {
return mountinfo.GetMounts(nil)
}

View File

@ -0,0 +1,18 @@
package mount
import (
"fmt"
"os"
"github.com/moby/sys/mountinfo"
)
func PidMountInfo(pid int) ([]*Info, error) {
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
if err != nil {
return nil, err
}
defer f.Close()
return mountinfo.GetMountsFromReader(f, nil)
}

View File

@ -0,0 +1,64 @@
package mount
// MakeShared ensures a mounted filesystem has the SHARED mount option enabled.
// See the supported options in flags.go for further reference.
func MakeShared(mountPoint string) error {
return ensureMountedAs(mountPoint, SHARED)
}
// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled.
// See the supported options in flags.go for further reference.
func MakeRShared(mountPoint string) error {
return ensureMountedAs(mountPoint, RSHARED)
}
// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled.
// See the supported options in flags.go for further reference.
func MakePrivate(mountPoint string) error {
return ensureMountedAs(mountPoint, PRIVATE)
}
// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option
// enabled. See the supported options in flags.go for further reference.
func MakeRPrivate(mountPoint string) error {
return ensureMountedAs(mountPoint, RPRIVATE)
}
// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled.
// See the supported options in flags.go for further reference.
func MakeSlave(mountPoint string) error {
return ensureMountedAs(mountPoint, SLAVE)
}
// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled.
// See the supported options in flags.go for further reference.
func MakeRSlave(mountPoint string) error {
return ensureMountedAs(mountPoint, RSLAVE)
}
// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option
// enabled. See the supported options in flags.go for further reference.
func MakeUnbindable(mountPoint string) error {
return ensureMountedAs(mountPoint, UNBINDABLE)
}
// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount
// option enabled. See the supported options in flags.go for further reference.
func MakeRUnbindable(mountPoint string) error {
return ensureMountedAs(mountPoint, RUNBINDABLE)
}
func ensureMountedAs(mnt string, flags int) error {
mounted, err := Mounted(mnt)
if err != nil {
return err
}
if !mounted {
if err := mount(mnt, mnt, "none", uintptr(BIND), ""); err != nil {
return err
}
}
return mount("", mnt, "none", uintptr(flags), "")
}

View File

@ -0,0 +1,34 @@
//go:build !windows
package mount
import (
"time"
"golang.org/x/sys/unix"
)
func unmount(target string, flags int) error {
var err error
for range 50 {
err = unix.Unmount(target, flags)
switch err {
case unix.EBUSY:
time.Sleep(50 * time.Millisecond)
continue
case unix.EINVAL, nil:
// Ignore "not mounted" error here. Note the same error
// can be returned if flags are invalid, so this code
// assumes that the flags value is always correct.
return nil
}
break
}
return &mountError{
op: "umount",
target: target,
flags: uintptr(flags),
err: err,
}
}

View File

@ -0,0 +1,7 @@
//go:build windows
package mount
func unmount(target string, flag int) error {
panic("Not implemented")
}

View File

@ -0,0 +1,5 @@
# reexec
The `reexec` package facilitates the busybox style reexec of the docker binary that we require because
of the forking limitations of using Go. Handlers can be registered with a name and the argv 0 of
the exec of the binary will be used to find and execute custom init paths.

View File

@ -0,0 +1,37 @@
//go:build freebsd
package reexec
import (
"context"
"os"
"os/exec"
"golang.org/x/sys/unix"
)
// Self returns the path to the current process's binary.
// Uses sysctl.
func Self() string {
path, err := unix.SysctlArgs("kern.proc.pathname", -1)
if err == nil {
return path
}
return os.Args[0]
}
// Command returns *exec.Cmd which has Path as current binary.
// For example if current binary is "docker" at "/usr/bin/", then cmd.Path will
// be set to "/usr/bin/docker".
func Command(args ...string) *exec.Cmd {
cmd := exec.Command(Self())
cmd.Args = args
return cmd
}
// CommandContext returns *exec.Cmd which has Path as current binary.
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
cmd := exec.CommandContext(ctx, Self())
cmd.Args = args
return cmd
}

View File

@ -0,0 +1,34 @@
//go:build linux
package reexec
import (
"context"
"os/exec"
)
// Self returns the path to the current process's binary.
// Returns "/proc/self/exe".
func Self() string {
return "/proc/self/exe"
}
// Command returns *exec.Cmd which has Path as current binary.
// This will use the in-memory version (/proc/self/exe) of the current binary,
// it is thus safe to delete or replace the on-disk binary (os.Args[0]).
func Command(args ...string) *exec.Cmd {
panicIfNotInitialized()
cmd := exec.Command(Self())
cmd.Args = args
return cmd
}
// CommandContext returns *exec.Cmd which has Path as current binary.
// This will use the in-memory version (/proc/self/exe) of the current binary,
// it is thus safe to delete or replace the on-disk binary (os.Args[0]).
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
panicIfNotInitialized()
cmd := exec.CommandContext(ctx, Self())
cmd.Args = args
return cmd
}

View File

@ -0,0 +1,32 @@
//go:build solaris || darwin
package reexec
import (
"context"
"os/exec"
)
// Self returns the path to the current process's binary.
// Uses os.Args[0].
func Self() string {
return naiveSelf()
}
// Command returns *exec.Cmd which has Path as current binary.
// For example if current binary is "docker" at "/usr/bin/", then cmd.Path will
// be set to "/usr/bin/docker".
func Command(args ...string) *exec.Cmd {
panicIfNotInitialized()
cmd := exec.Command(Self())
cmd.Args = args
return cmd
}
// CommandContext returns *exec.Cmd which has Path as current binary.
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
panicIfNotInitialized()
cmd := exec.CommandContext(ctx, Self())
cmd.Args = args
return cmd
}

View File

@ -0,0 +1,20 @@
//go:build !linux && !windows && !freebsd && !solaris && !darwin
package reexec
import (
"context"
"os/exec"
)
// Command is unsupported on operating systems apart from Linux, Windows, Solaris and Darwin.
func Command(args ...string) *exec.Cmd {
panicIfNotInitialized()
return nil
}
// CommandContext is unsupported on operating systems apart from Linux, Windows, Solaris and Darwin.
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
panicIfNotInitialized()
return nil
}

View File

@ -0,0 +1,34 @@
//go:build windows
package reexec
import (
"context"
"os/exec"
)
// Self returns the path to the current process's binary.
// Uses os.Args[0].
func Self() string {
return naiveSelf()
}
// Command returns *exec.Cmd which has Path as current binary.
// For example if current binary is "docker.exe" at "C:\", then cmd.Path will
// be set to "C:\docker.exe".
func Command(args ...string) *exec.Cmd {
panicIfNotInitialized()
cmd := exec.Command(Self())
cmd.Args = args
return cmd
}
// Command returns *exec.Cmd which has Path as current binary.
// For example if current binary is "docker.exe" at "C:\", then cmd.Path will
// be set to "C:\docker.exe".
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
panicIfNotInitialized()
cmd := exec.CommandContext(ctx, Self())
cmd.Args = args
return cmd
}

View File

@ -0,0 +1,66 @@
package reexec
import (
"fmt"
"os"
"os/exec"
"path/filepath"
)
var (
registeredInitializers = make(map[string]func())
initWasCalled = false
)
// Register adds an initialization func under the specified name
func Register(name string, initializer func()) {
if _, exists := registeredInitializers[name]; exists {
panic(fmt.Sprintf("reexec func already registered under name %q", name))
}
registeredInitializers[name] = initializer
}
// Init is called as the first part of the exec process and returns true if an
// initialization function was called.
func Init() bool {
initializer, exists := registeredInitializers[os.Args[0]]
initWasCalled = true
if exists {
initializer()
return true
}
return false
}
func panicIfNotInitialized() {
if !initWasCalled {
// The reexec package is used to run subroutines in
// subprocesses which would otherwise have unacceptable side
// effects on the main thread. If you found this error, then
// your program uses a package which needs to do this. In
// order for that to work, main() should start with this
// boilerplate, or an equivalent:
// if reexec.Init() {
// return
// }
panic("a library subroutine needed to run a subprocess, but reexec.Init() was not called in main()")
}
}
func naiveSelf() string {
name := os.Args[0]
if filepath.Base(name) == name {
if lp, err := exec.LookPath(name); err == nil {
return lp
}
}
// handle conversion of relative paths to absolute
if absName, err := filepath.Abs(name); err == nil {
return absName
}
// if we couldn't get absolute name, return original
// (NOTE: Go only errors on Abs() if os.Getwd fails)
return name
}

View File

@ -0,0 +1,234 @@
package regexp
import (
"io"
"regexp"
"sync"
)
// Regexp is a wrapper struct used for wrapping MustCompile regex expressions
// used as global variables. Using this structure helps speed the startup time
// of apps that want to use global regex variables. This library initializes them on
// first use as opposed to the start of the executable.
type Regexp struct {
*regexpStruct
}
type regexpStruct struct {
_ noCopy
once sync.Once
regexp *regexp.Regexp
val string
}
func Delayed(val string) Regexp {
re := &regexpStruct{
val: val,
}
if precompile {
re.regexp = regexp.MustCompile(re.val)
}
return Regexp{re}
}
func (re *regexpStruct) compile() {
if precompile {
return
}
re.once.Do(func() {
re.regexp = regexp.MustCompile(re.val)
})
}
func (re *regexpStruct) Expand(dst []byte, template []byte, src []byte, match []int) []byte {
re.compile()
return re.regexp.Expand(dst, template, src, match)
}
func (re *regexpStruct) ExpandString(dst []byte, template string, src string, match []int) []byte {
re.compile()
return re.regexp.ExpandString(dst, template, src, match)
}
func (re *regexpStruct) Find(b []byte) []byte {
re.compile()
return re.regexp.Find(b)
}
func (re *regexpStruct) FindAll(b []byte, n int) [][]byte {
re.compile()
return re.regexp.FindAll(b, n)
}
func (re *regexpStruct) FindAllIndex(b []byte, n int) [][]int {
re.compile()
return re.regexp.FindAllIndex(b, n)
}
func (re *regexpStruct) FindAllString(s string, n int) []string {
re.compile()
return re.regexp.FindAllString(s, n)
}
func (re *regexpStruct) FindAllStringIndex(s string, n int) [][]int {
re.compile()
return re.regexp.FindAllStringIndex(s, n)
}
func (re *regexpStruct) FindAllStringSubmatch(s string, n int) [][]string {
re.compile()
return re.regexp.FindAllStringSubmatch(s, n)
}
func (re *regexpStruct) FindAllStringSubmatchIndex(s string, n int) [][]int {
re.compile()
return re.regexp.FindAllStringSubmatchIndex(s, n)
}
func (re *regexpStruct) FindAllSubmatch(b []byte, n int) [][][]byte {
re.compile()
return re.regexp.FindAllSubmatch(b, n)
}
func (re *regexpStruct) FindAllSubmatchIndex(b []byte, n int) [][]int {
re.compile()
return re.regexp.FindAllSubmatchIndex(b, n)
}
func (re *regexpStruct) FindIndex(b []byte) (loc []int) {
re.compile()
return re.regexp.FindIndex(b)
}
func (re *regexpStruct) FindReaderIndex(r io.RuneReader) (loc []int) {
re.compile()
return re.regexp.FindReaderIndex(r)
}
func (re *regexpStruct) FindReaderSubmatchIndex(r io.RuneReader) []int {
re.compile()
return re.regexp.FindReaderSubmatchIndex(r)
}
func (re *regexpStruct) FindString(s string) string {
re.compile()
return re.regexp.FindString(s)
}
func (re *regexpStruct) FindStringIndex(s string) (loc []int) {
re.compile()
return re.regexp.FindStringIndex(s)
}
func (re *regexpStruct) FindStringSubmatch(s string) []string {
re.compile()
return re.regexp.FindStringSubmatch(s)
}
func (re *regexpStruct) FindStringSubmatchIndex(s string) []int {
re.compile()
return re.regexp.FindStringSubmatchIndex(s)
}
func (re *regexpStruct) FindSubmatch(b []byte) [][]byte {
re.compile()
return re.regexp.FindSubmatch(b)
}
func (re *regexpStruct) FindSubmatchIndex(b []byte) []int {
re.compile()
return re.regexp.FindSubmatchIndex(b)
}
func (re *regexpStruct) LiteralPrefix() (prefix string, complete bool) {
re.compile()
return re.regexp.LiteralPrefix()
}
func (re *regexpStruct) Longest() {
re.compile()
re.regexp.Longest()
}
func (re *regexpStruct) Match(b []byte) bool {
re.compile()
return re.regexp.Match(b)
}
func (re *regexpStruct) MatchReader(r io.RuneReader) bool {
re.compile()
return re.regexp.MatchReader(r)
}
func (re *regexpStruct) MatchString(s string) bool {
re.compile()
return re.regexp.MatchString(s)
}
func (re *regexpStruct) NumSubexp() int {
re.compile()
return re.regexp.NumSubexp()
}
func (re *regexpStruct) ReplaceAll(src, repl []byte) []byte {
re.compile()
return re.regexp.ReplaceAll(src, repl)
}
func (re *regexpStruct) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte {
re.compile()
return re.regexp.ReplaceAllFunc(src, repl)
}
func (re *regexpStruct) ReplaceAllLiteral(src, repl []byte) []byte {
re.compile()
return re.regexp.ReplaceAllLiteral(src, repl)
}
func (re *regexpStruct) ReplaceAllLiteralString(src, repl string) string {
re.compile()
return re.regexp.ReplaceAllLiteralString(src, repl)
}
func (re *regexpStruct) ReplaceAllString(src, repl string) string {
re.compile()
return re.regexp.ReplaceAllString(src, repl)
}
func (re *regexpStruct) ReplaceAllStringFunc(src string, repl func(string) string) string {
re.compile()
return re.regexp.ReplaceAllStringFunc(src, repl)
}
func (re *regexpStruct) Split(s string, n int) []string {
re.compile()
return re.regexp.Split(s, n)
}
func (re *regexpStruct) String() string {
re.compile()
return re.regexp.String()
}
func (re *regexpStruct) SubexpIndex(name string) int {
re.compile()
return re.regexp.SubexpIndex(name)
}
func (re *regexpStruct) SubexpNames() []string {
re.compile()
return re.regexp.SubexpNames()
}
// noCopy may be added to structs which must not be copied
// after the first use.
//
// See https://golang.org/issues/8005#issuecomment-190753527
// for details.
//
// Note that it must not be embedded, due to the Lock and Unlock methods.
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}

View File

@ -0,0 +1,5 @@
//go:build !regexp_precompile
package regexp
const precompile = false

View File

@ -0,0 +1,5 @@
//go:build regexp_precompile
package regexp
const precompile = true

View File

@ -0,0 +1,17 @@
package system
import (
"errors"
"os"
"syscall"
)
func Chmod(name string, mode os.FileMode) error {
err := os.Chmod(name, mode)
for err != nil && errors.Is(err, syscall.EINTR) {
err = os.Chmod(name, mode)
}
return err
}

View File

@ -0,0 +1,35 @@
package system
import (
"os"
"time"
)
// Chtimes changes the access time and modified time of a file at the given path
func Chtimes(name string, atime time.Time, mtime time.Time) error {
unixMinTime := time.Unix(0, 0)
unixMaxTime := maxTime
// If the modified time is prior to the Unix Epoch, or after the
// end of Unix Time, os.Chtimes has undefined behavior
// default to Unix Epoch in this case, just in case
if atime.Before(unixMinTime) || atime.After(unixMaxTime) {
atime = unixMinTime
}
if mtime.Before(unixMinTime) || mtime.After(unixMaxTime) {
mtime = unixMinTime
}
if err := os.Chtimes(name, atime, mtime); err != nil {
return err
}
// Take platform specific action for setting create time.
if err := setCTime(name, mtime); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,14 @@
//go:build !windows
package system
import (
"time"
)
// setCTime will set the create time on a file. On Unix, the create
// time is updated as a side effect of setting the modified time, so
// no action is required.
func setCTime(path string, ctime time.Time) error {
return nil
}

View File

@ -0,0 +1,28 @@
//go:build windows
package system
import (
"time"
"golang.org/x/sys/windows"
)
// setCTime will set the create time on a file. On Windows, this requires
// calling SetFileTime and explicitly including the create time.
func setCTime(path string, ctime time.Time) error {
ctimespec := windows.NsecToTimespec(ctime.UnixNano())
pathp, e := windows.UTF16PtrFromString(path)
if e != nil {
return e
}
h, e := windows.CreateFile(pathp,
windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil,
windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
if e != nil {
return e
}
defer windows.Close(h)
c := windows.NsecToFiletime(windows.TimespecToNsec(ctimespec))
return windows.SetFileTime(h, &c, nil, nil)
}

View File

@ -0,0 +1,8 @@
package system
import (
"errors"
)
// ErrNotSupportedPlatform means the platform is not supported.
var ErrNotSupportedPlatform = errors.New("platform and architecture is not supported")

View File

@ -0,0 +1,33 @@
package system
import (
"fmt"
"os/exec"
"syscall"
)
// GetExitCode returns the ExitStatus of the specified error if its type is
// exec.ExitError, returns 0 and an error otherwise.
func GetExitCode(err error) (int, error) {
exitCode := 0
if exiterr, ok := err.(*exec.ExitError); ok {
if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
return procExit.ExitStatus(), nil
}
}
return exitCode, fmt.Errorf("failed to get exit code")
}
// ProcessExitCode process the specified error and returns the exit status code
// if the error was of type exec.ExitError, returns nothing otherwise.
func ProcessExitCode(err error) (exitCode int) {
if err != nil {
var exiterr error
if exitCode, exiterr = GetExitCode(err); exiterr != nil {
// TODO: Fix this so we check the error's text.
// we've failed to retrieve exit code, so we set it to 127
exitCode = 127
}
}
return
}

View File

@ -0,0 +1,93 @@
//go:build freebsd
package system
import (
"os"
"unsafe"
"golang.org/x/sys/unix"
)
const (
EXTATTR_NAMESPACE_EMPTY = unix.EXTATTR_NAMESPACE_EMPTY
EXTATTR_NAMESPACE_USER = unix.EXTATTR_NAMESPACE_USER
EXTATTR_NAMESPACE_SYSTEM = unix.EXTATTR_NAMESPACE_SYSTEM
)
// ExtattrGetLink retrieves the value of the extended attribute identified by attrname
// in the given namespace and associated with the given path in the file system.
// If the path is a symbolic link, the extended attribute is retrieved from the link itself.
// Returns a []byte slice if the extattr is set and nil otherwise.
func ExtattrGetLink(path string, attrnamespace int, attrname string) ([]byte, error) {
size, errno := unix.ExtattrGetLink(path, attrnamespace, attrname,
uintptr(unsafe.Pointer(nil)), 0)
if errno != nil {
if errno == unix.ENOATTR {
return nil, nil
}
return nil, &os.PathError{Op: "extattr_get_link", Path: path, Err: errno}
}
if size == 0 {
return []byte{}, nil
}
dest := make([]byte, size)
size, errno = unix.ExtattrGetLink(path, attrnamespace, attrname,
uintptr(unsafe.Pointer(&dest[0])), size)
if errno != nil {
return nil, &os.PathError{Op: "extattr_get_link", Path: path, Err: errno}
}
return dest[:size], nil
}
// ExtattrSetLink sets the value of extended attribute identified by attrname
// in the given namespace and associated with the given path in the file system.
// If the path is a symbolic link, the extended attribute is set on the link itself.
func ExtattrSetLink(path string, attrnamespace int, attrname string, data []byte) error {
if len(data) == 0 {
data = []byte{} // ensure non-nil for empty data
}
if _, errno := unix.ExtattrSetLink(path, attrnamespace, attrname,
uintptr(unsafe.Pointer(&data[0])), len(data)); errno != nil {
return &os.PathError{Op: "extattr_set_link", Path: path, Err: errno}
}
return nil
}
// ExtattrListLink lists extended attributes associated with the given path
// in the specified namespace. If the path is a symbolic link, the attributes
// are listed from the link itself.
func ExtattrListLink(path string, attrnamespace int) ([]string, error) {
size, errno := unix.ExtattrListLink(path, attrnamespace,
uintptr(unsafe.Pointer(nil)), 0)
if errno != nil {
return nil, &os.PathError{Op: "extattr_list_link", Path: path, Err: errno}
}
if size == 0 {
return []string{}, nil
}
dest := make([]byte, size)
size, errno = unix.ExtattrListLink(path, attrnamespace,
uintptr(unsafe.Pointer(&dest[0])), size)
if errno != nil {
return nil, &os.PathError{Op: "extattr_list_link", Path: path, Err: errno}
}
var attrs []string
for i := 0; i < size; {
// Each attribute is preceded by a single byte length
length := int(dest[i])
i++
if i+length > size {
break
}
attrs = append(attrs, string(dest[i:i+length]))
i += length
}
return attrs, nil
}

View File

@ -0,0 +1,24 @@
//go:build !freebsd
package system
const (
EXTATTR_NAMESPACE_EMPTY = 0
EXTATTR_NAMESPACE_USER = 0
EXTATTR_NAMESPACE_SYSTEM = 0
)
// ExtattrGetLink is not supported on platforms other than FreeBSD.
func ExtattrGetLink(path string, attrnamespace int, attrname string) ([]byte, error) {
return nil, ErrNotSupportedPlatform
}
// ExtattrSetLink is not supported on platforms other than FreeBSD.
func ExtattrSetLink(path string, attrnamespace int, attrname string, data []byte) error {
return ErrNotSupportedPlatform
}
// ExtattrListLink is not supported on platforms other than FreeBSD.
func ExtattrListLink(path string, attrnamespace int) ([]string, error) {
return nil, ErrNotSupportedPlatform
}

View File

@ -0,0 +1,22 @@
package system
import (
"syscall"
"time"
"unsafe"
)
// maxTime is used by chtimes.
var maxTime time.Time
func init() {
// chtimes initialization
if unsafe.Sizeof(syscall.Timespec{}.Nsec) == 8 {
// This is a 64 bit timespec
// os.Chtimes limits time to the following
maxTime = time.Unix(0, 1<<63-1)
} else {
// This is a 32 bit timespec
maxTime = time.Unix(1<<31-1, 0)
}
}

View File

@ -0,0 +1,16 @@
package system
import "os"
// LCOWSupported determines if Linux Containers on Windows are supported.
// Note: This feature is in development (06/17) and enabled through an
// environment variable. At a future time, it will be enabled based
// on build number. @jhowardmsft
var lcowSupported = false
func init() {
// LCOW initialization
if os.Getenv("LCOW_SUPPORTED") != "" {
lcowSupported = true
}
}

View File

@ -0,0 +1,55 @@
//go:build freebsd
package system
import (
"unsafe"
"golang.org/x/sys/unix"
)
// Flag values from <sys/stat.h>
const (
/*
* Definitions of flags stored in file flags word.
*
* Super-user and owner changeable flags.
*/
UF_SETTABLE uint32 = 0x0000ffff /* mask of owner changeable flags */
UF_NODUMP uint32 = 0x00000001 /* do not dump file */
UF_IMMUTABLE uint32 = 0x00000002 /* file may not be changed */
UF_APPEND uint32 = 0x00000004 /* writes to file may only append */
UF_OPAQUE uint32 = 0x00000008 /* directory is opaque wrt. union */
UF_NOUNLINK uint32 = 0x00000010 /* file may not be removed or renamed */
UF_SYSTEM uint32 = 0x00000080 /* Windows system file bit */
UF_SPARSE uint32 = 0x00000100 /* sparse file */
UF_OFFLINE uint32 = 0x00000200 /* file is offline */
UF_REPARSE uint32 = 0x00000400 /* Windows reparse point file bit */
UF_ARCHIVE uint32 = 0x00000800 /* file needs to be archived */
UF_READONLY uint32 = 0x00001000 /* Windows readonly file bit */
/* This is the same as the MacOS X definition of UF_HIDDEN. */
UF_HIDDEN uint32 = 0x00008000 /* file is hidden */
/*
* Super-user changeable flags.
*/
SF_SETTABLE uint32 = 0xffff0000 /* mask of superuser changeable flags */
SF_ARCHIVED uint32 = 0x00010000 /* file is archived */
SF_IMMUTABLE uint32 = 0x00020000 /* file may not be changed */
SF_APPEND uint32 = 0x00040000 /* writes to file may only append */
SF_NOUNLINK uint32 = 0x00100000 /* file may not be removed or renamed */
SF_SNAPSHOT uint32 = 0x00200000 /* snapshot inode */
)
func Lchflags(path string, flags uint32) error {
p, err := unix.BytePtrFromString(path)
if err != nil {
return err
}
_, _, e1 := unix.Syscall(unix.SYS_LCHFLAGS, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
if e1 != 0 {
return e1
}
return nil
}

View File

@ -0,0 +1,20 @@
package system
import (
"os"
"syscall"
)
func Lchown(name string, uid, gid int) error {
err := syscall.Lchown(name, uid, gid)
for err == syscall.EINTR {
err = syscall.Lchown(name, uid, gid)
}
if err != nil {
return &os.PathError{Op: "lchown", Path: name, Err: err}
}
return nil
}

View File

@ -0,0 +1,8 @@
//go:build !windows
package system
// LCOWSupported returns true if Linux containers on Windows are supported.
func LCOWSupported() bool {
return false
}

View File

@ -0,0 +1,6 @@
package system
// LCOWSupported returns true if Linux containers on Windows are supported.
func LCOWSupported() bool {
return lcowSupported
}

View File

@ -0,0 +1,20 @@
//go:build !windows
package system
import (
"os"
"syscall"
)
// Lstat takes a path to a file and returns
// a system.StatT type pertaining to that file.
//
// Throws an error if the file does not exist
func Lstat(path string) (*StatT, error) {
s := &syscall.Stat_t{}
if err := syscall.Lstat(path, s); err != nil {
return nil, &os.PathError{Op: "Lstat", Path: path, Err: err}
}
return fromStatT(s)
}

View File

@ -0,0 +1,14 @@
package system
import "os"
// Lstat calls os.Lstat to get a fileinfo interface back.
// This is then copied into our own locally defined structure.
func Lstat(path string) (*StatT, error) {
fi, err := os.Lstat(path)
if err != nil {
return nil, err
}
return fromStatT(&fi)
}

View File

@ -0,0 +1,17 @@
package system
// MemInfo contains memory statistics of the host system.
type MemInfo struct {
// Total usable RAM (i.e. physical RAM minus a few reserved bits and the
// kernel binary code).
MemTotal int64
// Amount of free memory.
MemFree int64
// Total amount of swap space available.
SwapTotal int64
// Amount of swap space that is currently unused.
SwapFree int64
}

View File

@ -0,0 +1,85 @@
//go:build freebsd && cgo
package system
import (
"errors"
"fmt"
"unsafe"
"golang.org/x/sys/unix"
)
// #include <unistd.h>
// #include <sys/vmmeter.h>
// #include <sys/sysctl.h>
// #include <vm/vm_param.h>
import "C"
func getMemInfo() (int64, int64, error) {
data, err := unix.SysctlRaw("vm.vmtotal")
if err != nil {
return -1, -1, fmt.Errorf("can't get kernel info: %w", err)
}
if len(data) != C.sizeof_struct_vmtotal {
return -1, -1, fmt.Errorf("unexpected vmtotal size %d", len(data))
}
total := (*C.struct_vmtotal)(unsafe.Pointer(&data[0]))
pagesize := int64(C.sysconf(C._SC_PAGESIZE))
npages := int64(C.sysconf(C._SC_PHYS_PAGES))
return pagesize * npages, pagesize * int64(total.t_free), nil
}
func getSwapInfo() (int64, int64, error) {
var (
total int64 = 0
used int64 = 0
)
swapCount, err := unix.SysctlUint32("vm.nswapdev")
if err != nil {
return -1, -1, fmt.Errorf("reading vm.nswapdev: %w", err)
}
for i := 0; i < int(swapCount); i++ {
data, err := unix.SysctlRaw("vm.swap_info", i)
if err != nil {
return -1, -1, fmt.Errorf("reading vm.swap_info.%d: %w", i, err)
}
if len(data) != C.sizeof_struct_xswdev {
return -1, -1, fmt.Errorf("unexpected swap_info size %d", len(data))
}
xsw := (*C.struct_xswdev)(unsafe.Pointer(&data[0]))
total += int64(xsw.xsw_nblks)
used += int64(xsw.xsw_used)
}
pagesize := int64(C.sysconf(C._SC_PAGESIZE))
return pagesize * total, pagesize * (total - used), nil
}
// ReadMemInfo retrieves memory statistics of the host system and returns a
//
// MemInfo type.
func ReadMemInfo() (*MemInfo, error) {
MemTotal, MemFree, err := getMemInfo()
if err != nil {
return nil, fmt.Errorf("getting memory totals %w", err)
}
SwapTotal, SwapFree, err := getSwapInfo()
if err != nil {
return nil, fmt.Errorf("getting swap totals %w", err)
}
if MemTotal < 0 || MemFree < 0 || SwapTotal < 0 || SwapFree < 0 {
return nil, errors.New("getting system memory info")
}
meminfo := &MemInfo{}
// Total memory is total physical memory less than memory locked by kernel
meminfo.MemTotal = MemTotal
meminfo.MemFree = MemFree
meminfo.SwapTotal = SwapTotal
meminfo.SwapFree = SwapFree
return meminfo, nil
}

View File

@ -0,0 +1,65 @@
package system
import (
"bufio"
"io"
"os"
"strconv"
"strings"
"github.com/docker/go-units"
)
// ReadMemInfo retrieves memory statistics of the host system and returns a
// MemInfo type.
func ReadMemInfo() (*MemInfo, error) {
file, err := os.Open("/proc/meminfo")
if err != nil {
return nil, err
}
defer file.Close()
return parseMemInfo(file)
}
// parseMemInfo parses the /proc/meminfo file into
// a MemInfo object given an io.Reader to the file.
// Throws error if there are problems reading from the file
func parseMemInfo(reader io.Reader) (*MemInfo, error) {
meminfo := &MemInfo{}
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
// Expected format: ["MemTotal:", "1234", "kB"]
parts := strings.Fields(scanner.Text())
// Sanity checks: Skip malformed entries.
if len(parts) < 3 || parts[2] != "kB" {
continue
}
// Convert to bytes.
size, err := strconv.Atoi(parts[1])
if err != nil {
continue
}
bytes := int64(size) * units.KiB
switch parts[0] {
case "MemTotal:":
meminfo.MemTotal = bytes
case "MemFree:":
meminfo.MemFree = bytes
case "SwapTotal:":
meminfo.SwapTotal = bytes
case "SwapFree:":
meminfo.SwapFree = bytes
}
}
// Handle errors that may have occurred during the reading of the file.
if err := scanner.Err(); err != nil {
return nil, err
}
return meminfo, nil
}

View File

@ -0,0 +1,129 @@
//go:build solaris && cgo
package system
import (
"fmt"
"unsafe"
)
// #cgo CFLAGS: -std=c99
// #cgo LDFLAGS: -lkstat
// #include <unistd.h>
// #include <stdlib.h>
// #include <stdio.h>
// #include <kstat.h>
// #include <sys/swap.h>
// #include <sys/param.h>
// struct swaptable *allocSwaptable(int num) {
// struct swaptable *st;
// struct swapent *swapent;
// st = (struct swaptable *)malloc(num * sizeof(swapent_t) + sizeof (int));
// swapent = st->swt_ent;
// for (int i = 0; i < num; i++,swapent++) {
// swapent->ste_path = (char *)malloc(MAXPATHLEN * sizeof (char));
// }
// st->swt_n = num;
// return st;
//}
// void freeSwaptable (struct swaptable *st) {
// struct swapent *swapent = st->swt_ent;
// for (int i = 0; i < st->swt_n; i++,swapent++) {
// free(swapent->ste_path);
// }
// free(st);
// }
// swapent_t getSwapEnt(swapent_t *ent, int i) {
// return ent[i];
// }
// int64_t getPpKernel() {
// int64_t pp_kernel = 0;
// kstat_ctl_t *ksc;
// kstat_t *ks;
// kstat_named_t *knp;
// kid_t kid;
//
// if ((ksc = kstat_open()) == NULL) {
// return -1;
// }
// if ((ks = kstat_lookup(ksc, "unix", 0, "system_pages")) == NULL) {
// return -1;
// }
// if (((kid = kstat_read(ksc, ks, NULL)) == -1) ||
// ((knp = kstat_data_lookup(ks, "pp_kernel")) == NULL)) {
// return -1;
// }
// switch (knp->data_type) {
// case KSTAT_DATA_UINT64:
// pp_kernel = knp->value.ui64;
// break;
// case KSTAT_DATA_UINT32:
// pp_kernel = knp->value.ui32;
// break;
// }
// pp_kernel *= sysconf(_SC_PAGESIZE);
// return (pp_kernel > 0 ? pp_kernel : -1);
// }
import "C"
// Get the system memory info using sysconf same as prtconf
func getTotalMem() int64 {
pagesize := C.sysconf(C._SC_PAGESIZE)
npages := C.sysconf(C._SC_PHYS_PAGES)
return int64(pagesize * npages)
}
func getFreeMem() int64 {
pagesize := C.sysconf(C._SC_PAGESIZE)
npages := C.sysconf(C._SC_AVPHYS_PAGES)
return int64(pagesize * npages)
}
// ReadMemInfo retrieves memory statistics of the host system and returns a
//
// MemInfo type.
func ReadMemInfo() (*MemInfo, error) {
ppKernel := C.getPpKernel()
MemTotal := getTotalMem()
MemFree := getFreeMem()
SwapTotal, SwapFree, err := getSysSwap()
if ppKernel < 0 || MemTotal < 0 || MemFree < 0 || SwapTotal < 0 ||
SwapFree < 0 {
return nil, fmt.Errorf("getting system memory info %w", err)
}
meminfo := &MemInfo{}
// Total memory is total physical memory less than memory locked by kernel
meminfo.MemTotal = MemTotal - int64(ppKernel)
meminfo.MemFree = MemFree
meminfo.SwapTotal = SwapTotal
meminfo.SwapFree = SwapFree
return meminfo, nil
}
func getSysSwap() (int64, int64, error) {
var tSwap int64
var fSwap int64
var diskblksPerPage int64
num, err := C.swapctl(C.SC_GETNSWP, nil)
if err != nil {
return -1, -1, err
}
st := C.allocSwaptable(num)
_, err = C.swapctl(C.SC_LIST, unsafe.Pointer(st))
if err != nil {
C.freeSwaptable(st)
return -1, -1, err
}
diskblksPerPage = int64(C.sysconf(C._SC_PAGESIZE) >> C.DEV_BSHIFT)
for i := 0; i < int(num); i++ {
swapent := C.getSwapEnt(&st.swt_ent[0], C.int(i))
tSwap += int64(swapent.ste_pages) * diskblksPerPage
fSwap += int64(swapent.ste_free) * diskblksPerPage
}
C.freeSwaptable(st)
return tSwap, fSwap, nil
}

View File

@ -0,0 +1,8 @@
//go:build !linux && !windows && !solaris && !(freebsd && cgo)
package system
// ReadMemInfo is not supported on platforms other than linux and windows.
func ReadMemInfo() (*MemInfo, error) {
return nil, ErrNotSupportedPlatform
}

View File

@ -0,0 +1,46 @@
package system
import (
"unsafe"
"golang.org/x/sys/windows"
)
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx")
)
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx
type memorystatusex struct {
dwLength uint32
dwMemoryLoad uint32
ullTotalPhys uint64
ullAvailPhys uint64
ullTotalPageFile uint64
ullAvailPageFile uint64
ullTotalVirtual uint64
ullAvailVirtual uint64
ullAvailExtendedVirtual uint64
}
// ReadMemInfo retrieves memory statistics of the host system and returns a
//
// MemInfo type.
func ReadMemInfo() (*MemInfo, error) {
msi := &memorystatusex{
dwLength: 64,
}
r1, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msi)))
if r1 == 0 {
return &MemInfo{}, nil
}
return &MemInfo{
MemTotal: int64(msi.ullTotalPhys),
MemFree: int64(msi.ullAvailPhys),
SwapTotal: int64(msi.ullTotalPageFile),
SwapFree: int64(msi.ullAvailPageFile),
}, nil
}

Some files were not shown because too many files have changed in this diff Show More