Compare commits

..

246 Commits
v1.1.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 febb2eb0f0 v1.2.1
* Cirrus: update to latest Fedora image
* defer event processing
* Cirrus: Use images from automation_images
* vendor gobpf
* Fix problems found by codespell
* Make annotation pattern match more strictly

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-01-27 13:16:07 +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
Valentin Rothberg 8c656034bf Cirrus: update to latest Fedora image
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-01-27 12:56:11 +01:00
Valentin Rothberg f7e344f8ce defer event processing
Defer the processing of events when we are done tracing.  This relaxes
the tracing goroutine a bit reduces the risk of potentially loosing
messages.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-01-27 12:55:17 +01:00
Chris Evich 1dd355b45c Cirrus: Use images from automation_images
Previously, VM Images were built from a side-band process tacked onto
containers/podman automation. This has recently been split off into it's
own repository containers/automation_images. This PR makes use of
freshly built images produced using the new workflow. Additionally, to
support testing of a runc -> crun transition, both packages are
installed in the newly referenced images.

Signed-off-by: Chris Evich <cevich@redhat.com>
2021-01-27 12:55:17 +01:00
Valentin Rothberg 73f4c1da35
Merge pull request #70 from weirdwiz/master
Update gobpf to support bcc 0.17.0
2021-01-17 15:33:48 +01:00
Divyansh Kamboj 3b5f1881e1 vendor gobpf
fixes #69

Signed-off-by: Divyansh Kamboj <kambojdivyansh2000@gmail.com>
2021-01-16 19:44:10 +05:30
Valentin Rothberg e86c7e6cb1
Merge pull request #66 from rhatdan/codespell
Fix problems found by codespell
2020-09-11 18:17:43 +02:00
Daniel J Walsh 2150d6524a
Fix problems found by codespell
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2020-09-11 10:32:55 -04:00
Valentin Rothberg e6400c50dc
Merge pull request #65 from rhafer/re_anchors
Make annotation pattern match more strictly
2020-09-10 17:03:53 +02:00
Ralf Haferkamp 6ab18cde1b Make annotation pattern match more strictly
The annotation name is supposed to be a regex. Add anchors and escape
characters to use and exact match for the annotation.

Signed-off-by: Ralf Haferkamp <rhafer@suse.com>
2020-09-10 15:41:53 +02:00
Valentin Rothberg 9f10df075c
Merge pull request #61 from vrothberg/v1.2.0
V1.2.0
2020-08-12 11:37:27 +02:00
Valentin Rothberg 9acbb81149 bump to v1.2.1-dev
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-08-12 11:28:14 +02:00
Valentin Rothberg f6ca81985f v1.2.0
* README: link to the enable sysadmin article
* Remove unused version variable from spec
* Improve README.md and replace black/whitelist references
* Automatically add seccomp architecture from runtime.GOARCH
* Switch to seccomp/containers-golang instead of docker types

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-08-12 11:27:53 +02:00
Valentin Rothberg c5e3818aab
Merge pull request #60 from vrothberg/update-readme
README: link to the enable sysadmin article
2020-08-12 11:25:23 +02:00
Valentin Rothberg 23836342cf README: link to the enable sysadmin article
The article [1] gives some nice background on the history of the hook
and further details that we do not need repeat here.

[1] https://www.redhat.com/sysadmin/container-security-seccomp

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-08-12 11:16:05 +02:00
Valentin Rothberg 9ea9e07037
Merge pull request #59 from saschagrunert/package-version
Remove unused version variable from spec
2020-08-12 11:13:09 +02:00
Sascha Grunert 100ce3a292
Remove unused version variable from spec
The version is now managed directly by a VERSION file and does not need
to be injected any more.

Signed-off-by: Sascha Grunert <sgrunert@suse.com>
2020-08-12 11:02:06 +02:00
Valentin Rothberg c7df7c4f65
Merge pull request #57 from saschagrunert/readme
Improve README.md and replace black/whitelist references
2020-08-12 10:23:52 +02:00
Sascha Grunert 817da261f9
Improve README.md and replace black/whitelist references
This adds some small improvements to the README.md as well as replacing
the blacklist and whitelist with "allow" and "deny".

Signed-off-by: Sascha Grunert <sgrunert@suse.com>
2020-08-12 10:00:31 +02:00
Valentin Rothberg b2bc06aba8
Merge pull request #56 from saschagrunert/goarch
Automatically add seccomp architecture from runtime.GOARCH
2020-08-12 09:39:43 +02:00
Sascha Grunert f0d6307b00
Automatically add seccomp architecture from runtime.GOARCH
We now choose the value of `runtime.GOARCH` to automatically add the
architecture to the output profile if not already present.

Signed-off-by: Sascha Grunert <sgrunert@suse.com>
2020-08-12 08:29:54 +02:00
Valentin Rothberg b14a9214a3
Merge pull request #53 from saschagrunert/types
Switch to seccomp/containers-golang instead of docker types
2020-08-03 10:05:24 +02:00
Sascha Grunert 2bee3bb8aa
Switch to seccomp/containers-golang instead of docker types
We should use the types from `github.com/seccomp/containers-golang`
instead of the dockers API types to stay inside the ecosystem.

The unnecessary dependencies have been removed as well from go.mod.

Signed-off-by: Sascha Grunert <sgrunert@suse.com>
2020-08-03 09:55:39 +02:00
Valentin Rothberg 1d6c433812
Merge pull request #52 from vrothberg/1.1.2
1.1.2
2020-07-17 15:21:07 +02:00
Valentin Rothberg ade24a0cf7 bump to v1.1.3-dev
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-07-17 14:13:23 +02:00
Valentin Rothberg 85b2b3704d v1.1.2
* make loading kheaders non-fatal
* don't block on stopping the perf map
* made first letters lowercase to match style
* clarified relative path error message and fixed incorrect variable in output file message

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-07-17 14:13:04 +02:00
Daniel J Walsh 33b553f78f
Merge pull request #51 from vrothberg/fix-bz-1857606
make loading kheaders non-fatal
2020-07-17 08:07:15 -04:00
Valentin Rothberg 9b805a9a37 make loading kheaders non-fatal
The `kheaders` kernel module was introduced with Linux 5.2 and allows
for accessign kernel headers via the proc FS.  The target use case are
eBPF tools without requiring the headers to be stored on the file
systems but to ship them directly with the kernel.

While this enabled running the seccomp hook in new environments, we also
ran into issues on older kernel, for instance on RHEL.  Those systems
have the kernel headers on disk, so we don't need to load the `kheaders`
module.

To enable the hook on older systems, make the modprobe error non-fatal
and only log it but continue tracing with the expectation that the
kernel headers are accessible on disk, so we can compile the eBPF
tracer.

Fixes: bugzilla.redhat.com/show_bug.cgi?id=1857606
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-07-17 13:28:02 +02:00
Valentin Rothberg 1cf35fbdc5
Merge pull request #50 from vrothberg/perf-stop
don't block on stopping the perf map
2020-07-17 13:27:36 +02:00
Valentin Rothberg 9c75b4cdcd don't block on stopping the perf map
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-07-16 12:32:32 +02:00
Valentin Rothberg 9f0e26560a
Merge pull request #49 from doctormay6/master
Clarified error message for relative paths
2020-07-16 12:10:05 +02:00
C.J. May 544d5124de made first letters lowercase to match style 2020-07-15 14:01:37 -05:00
C.J. May 54693c9b5c clarified relative path error message and fixed incorrect variable in output file message 2020-07-15 13:58:35 -05:00
Valentin Rothberg 33508af811
Merge pull request #47 from vrothberg/v1.1.1
V1.1.1
2020-06-18 15:19:03 +02:00
Valentin Rothberg e1c55123fd bump to v1.1.2-dev
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-06-18 15:07:56 +02:00
680 changed files with 117116 additions and 40742 deletions

View File

@ -10,9 +10,9 @@ env:
HOME: "/root" # not set by default
GOCACHE: "${HOME}/.cache/go-build"
# VM Images are maintained in the libpod repo.
_BUILT_IMAGE_SUFFIX: "libpod-6224667180531712" # From the packer output of 'build_vm_images_script'
FEDORA_CACHE_IMAGE_NAME: "fedora-32-${_BUILT_IMAGE_SUFFIX}"
# VM Image built in containers/automation_images
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,16 +1,16 @@
[![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
OCI hooks to generate seccomp profiles by tracing the syscalls made by the container. The generated profile would whitelist all the syscalls made and blacklist every other syscall.
This project provides an OCI hook to generate seccomp profiles by tracing the syscalls made by the container. The generated profile would allow all the syscalls made and deny every other syscall.
The syscalls are traced by launching a binary by using the prestart OCI hook. The binary started spawns a child process which attaches function `enter_trace` to the `raw_syscalls:sys_enter` tracepoint using eBPF. The function looks at all the syscalls made on the system and writes the syscalls which have the same PID namespace as the container to the perf buffer. The perf buffer is read by the process in the userspace and generates a seccomp profile when the container exits.
There are a few limitations to this approach:
* Needs CAP_SYS_ADMIN to run
* Needs `CAP_SYS_ADMIN` to run
* Compiles C code on the fly
* Cannot use podman run --rm along with this ability
* Cannot use `podman run --rm` along with this ability
To build it, we need extra dependencies namely bcc-devel and kernel-headers for Fedora and bcc-tools and linux-headers-[..] for Ubuntu.
@ -21,3 +21,7 @@ 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.1.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=()
@ -25,7 +24,7 @@ case "$OS_RELEASE_ID" in
ooe.sh dnf update -y
fi
# Installed AND separetly querried/displayed
# Installed AND separately querried/displayed
CRITICAL_PKGS+=(\
bcc
bcc-devel
@ -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
32)
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

44
ebpf.go
View File

@ -14,11 +14,10 @@ type event struct {
// the source is a bpf program compiled at runtime. Some macro's like
// BPF_HASH and BPF_PERF_OUTPUT are expanded during compilation
// by bcc. $PARENT_PID get's replaced before compilation with the PID of the container
// by bcc. $PARENT_PID gets replaced before compilation with the PID of the container
// 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;
}

34
go.mod
View File

@ -1,16 +1,28 @@
module github.com/containers/oci-seccomp-bpf-hook
go 1.12
go 1.23.0
toolchain go1.23.8
require (
github.com/docker/docker v1.13.1
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/iovisor/gobpf v0.0.0-20200504095308-90dbbdfb1358
github.com/opencontainers/runtime-spec v1.0.2
github.com/pkg/errors v0.9.1
github.com/seccomp/libseccomp-golang v0.9.1
github.com/sirupsen/logrus v1.6.0
github.com/stretchr/testify v1.4.0
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f // 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
)

81
go.sum
View File

@ -1,36 +1,55 @@
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/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/iovisor/gobpf v0.0.0-20200504095308-90dbbdfb1358 h1:Jwlf8GAKb98CrR9WZbUdpe82++USxerHAHqLVt3C8zc=
github.com/iovisor/gobpf v0.0.0-20200504095308-90dbbdfb1358/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.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
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/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/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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
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-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/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 (asumes $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}Transfering 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,14 +5,15 @@ import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"log/syslog"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
@ -20,10 +21,10 @@ import (
"syscall"
"time"
"github.com/docker/docker/api/types"
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"
seccomp "github.com/seccomp/libseccomp-golang"
"github.com/sirupsen/logrus"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
@ -56,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")
@ -108,9 +114,9 @@ func modprobe(module string) error {
// detachAndTrace re-executes the current executable to "fork" in go-ish way and
// traces the provided PID.
func detachAndTrace() error {
logrus.Info("Loading kheaders module")
logrus.Info("Trying to load `kheaders` module")
if err := modprobe("kheaders"); err != nil {
return errors.Wrap(err, "error loading kheaders module")
logrus.Infof("Loading `kheaders` failed, continuing in hope kernel headers reside on disk: %v", err)
}
// Read the State spec from stdin and unmarshal it.
@ -123,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.
@ -158,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 {
@ -182,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.
@ -190,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
@ -203,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)
@ -222,46 +228,40 @@ 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.
wg.Add(1)
var events []event
go func() {
defer wg.Done()
recordSyscalls := false
var e event
local := []event{}
for data := range channel {
var e event
if err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &e); err != nil {
// Return in case of an error. Otherwise, we
// could miss stop event and run into an
@ -273,39 +273,49 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
// The BPF program is done tracing, so we can stop
// reading from the perf buffer.
if e.StopTracing {
// Pointing events at the very end should relax
// the memory management a bit as we don't have
// to constantly sync across routines.
events = local
return
}
name, err := syscallIDtoName(e.ID)
if err != nil {
logrus.Errorf("error getting the name for syscall ID %d", e.ID)
}
// 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]++
}
// We are in a hurry to not lose messages, so defer
// processing the events when we're done tracing.
local = append(local, e)
}
}()
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 progam has finished")
logrus.Info("BPF program has finished")
// Post-process the recorded events and extract the syscall names.
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[name]++
}
logrus.Info("PerfMap Stop")
perfMap.Stop()
go perfMap.Stop()
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
}
@ -317,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)
}
}
@ -340,6 +350,10 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
outputProfile = inputProfile
outputProfile.DefaultAction = types.ActErrno
if err := appendArchIfNotAlreadyIncluded(runtime.GOARCH, &outputProfile); err != nil {
return fmt.Errorf("appending architecture to output profile: %v", err)
}
outputProfile.Syscalls = append(outputProfile.Syscalls, &types.Syscall{
Action: types.ActAllow,
Names: names,
@ -348,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
}
@ -361,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 {
@ -369,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, "paths 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, "paths must be absolute: %q", inputFile)
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
@ -420,3 +434,18 @@ func syscallInProfile(profile *types.Seccomp, syscall string) bool {
}
return false
}
func appendArchIfNotAlreadyIncluded(goArch string, profile *types.Seccomp) error {
targetArch, err := types.GoArchToSeccompArch(goArch)
if err != nil {
return fmt.Errorf("determine target architecture: %v", err)
}
for _, arch := range profile.Architectures {
if arch == targetArch {
// architecture already part of the profile
return nil
}
}
profile.Architectures = append(profile.Architectures, targetArch)
return nil
}

View File

@ -9,7 +9,7 @@
},
"when": {
"annotations": {
"io.containers.trace-syscall": ".*"
"^io\\.containers\\.trace-syscall$": ".*"
}
},
"stages": [

View File

@ -71,7 +71,7 @@ export BUILDTAGS=""
BUILDTAGS=$BUILDTAGS make
%install
%{__make} DESTDIR=%{buildroot} PREFIX=%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} OCI-SECCOMP-BPF_VERSION=%{version} install
%{__make} DESTDIR=%{buildroot} PREFIX=%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} install
%check
%if 0%{?with_check} && 0%{?with_unit_test} && 0%{?with_devel}
@ -102,6 +102,9 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath}
%{_mandir}/man1/%{name}.1*
%changelog
* Wed Aug 12 2020 Sascha Grunert <sgrunert@suse.com>
- remove unused version env var on build
* Mon Sep 23 2019 Jindrich Novy <jnovy@redhat.com> - 0.0.1-0.1.gite200a9ed
- fix spec file and build

View File

@ -2,11 +2,11 @@ package main
import (
"encoding/json"
"io/ioutil"
"os"
"runtime"
"testing"
"github.com/docker/docker/api/types"
types "github.com/containers/common/pkg/seccomp"
"github.com/stretchr/testify/assert"
)
@ -14,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")
}
@ -52,3 +52,55 @@ func TestParseAnnotation(t *testing.T) {
assert.NotNil(t, err)
}
}
func TestAppendArchIfNotAlreadyIncluded(t *testing.T) {
if runtime.GOARCH != "amd64" {
t.Skip("Test runs only reliable on amd64 arch")
}
currentArch, err := types.GoArchToSeccompArch(runtime.GOARCH)
assert.Nil(t, err)
for _, tc := range []struct {
profile types.Seccomp
goArch string
expect func(error, types.Seccomp)
}{
{
profile: types.Seccomp{},
goArch: runtime.GOARCH,
expect: func(err error, profile types.Seccomp) {
assert.Nil(t, err)
assert.Len(t, profile.Architectures, 1)
},
},
{
profile: types.Seccomp{
Architectures: []types.Arch{currentArch},
},
goArch: runtime.GOARCH,
expect: func(err error, profile types.Seccomp) {
assert.Nil(t, err)
assert.Len(t, profile.Architectures, 1)
},
},
{
profile: types.Seccomp{
Architectures: []types.Arch{types.ArchMIPS, types.ArchARM},
},
goArch: runtime.GOARCH,
expect: func(err error, profile types.Seccomp) {
assert.Nil(t, err)
assert.Len(t, profile.Architectures, 3)
},
},
{
profile: types.Seccomp{},
goArch: "wrong",
expect: func(err error, profile types.Seccomp) {
assert.NotNil(t, err)
assert.Empty(t, profile.Architectures)
},
},
} {
tc.expect(appendArchIfNotAlreadyIncluded(tc.goArch, &tc.profile), tc.profile)
}
}

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

@ -0,0 +1,953 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2018 Docker, Inc.
package seccomp
import (
"golang.org/x/sys/unix"
)
func arches() []Architecture {
return []Architecture{
{
Arch: ArchX86_64,
SubArches: []Arch{ArchX86, ArchX32},
},
{
Arch: ArchAARCH64,
SubArches: []Arch{ArchARM},
},
{
Arch: ArchMIPS64,
SubArches: []Arch{ArchMIPS, ArchMIPS64N32},
},
{
Arch: ArchMIPS64N32,
SubArches: []Arch{ArchMIPS, ArchMIPS64},
},
{
Arch: ArchMIPSEL64,
SubArches: []Arch{ArchMIPSEL, ArchMIPSEL64N32},
},
{
Arch: ArchMIPSEL64N32,
SubArches: []Arch{ArchMIPSEL, ArchMIPSEL64},
},
{
Arch: ArchS390X,
SubArches: []Arch{ArchS390},
},
}
}
// DefaultProfile defines the allowlist for the default seccomp profile.
func DefaultProfile() *Seccomp {
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",
"_newselect",
"accept",
"accept4",
"access",
"adjtimex",
"alarm",
"bind",
"brk",
"capget",
"capset",
"chdir",
"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",
"dup",
"dup2",
"dup3",
"epoll_create",
"epoll_create1",
"epoll_ctl",
"epoll_ctl_old",
"epoll_pwait",
"epoll_pwait2",
"epoll_wait",
"epoll_wait_old",
"eventfd",
"eventfd2",
"execve",
"execveat",
"exit",
"exit_group",
"faccessat",
"faccessat2",
"fadvise64",
"fadvise64_64",
"fallocate",
"fanotify_mark",
"fchdir",
"fchmod",
"fchmodat",
"fchmodat2",
"fchown",
"fchown32",
"fchownat",
"fcntl",
"fcntl64",
"fdatasync",
"fgetxattr",
"flistxattr",
"flock",
"fork",
"fremovexattr",
"fsconfig",
"fsetxattr",
"fsmount",
"fsopen",
"fspick",
"fstat",
"fstat64",
"fstatat64",
"fstatfs",
"fstatfs64",
"fsync",
"ftruncate",
"ftruncate64",
"futex",
"futex_time64",
"futimesat",
"get_mempolicy",
"get_robust_list",
"get_thread_area",
"getcpu",
"getcwd",
"getdents",
"getdents64",
"getegid",
"getegid32",
"geteuid",
"geteuid32",
"getgid",
"getgid32",
"getgroups",
"getgroups32",
"getitimer",
"getpeername",
"getpgid",
"getpgrp",
"getpid",
"getppid",
"getpriority",
"getrandom",
"getresgid",
"getresgid32",
"getresuid",
"getresuid32",
"getrlimit",
"getrusage",
"getsid",
"getsockname",
"getsockopt",
"gettid",
"gettimeofday",
"getuid",
"getuid32",
"getxattr",
"inotify_add_watch",
"inotify_init",
"inotify_init1",
"inotify_rm_watch",
"io_cancel",
"io_destroy",
"io_getevents",
"io_setup",
"io_submit",
"ioctl",
"ioprio_get",
"ioprio_set",
"ipc",
"keyctl",
"kill",
"landlock_add_rule",
"landlock_create_ruleset",
"landlock_restrict_self",
"lchown",
"lchown32",
"lgetxattr",
"link",
"linkat",
"listen",
"listxattr",
"llistxattr",
"lremovexattr",
"lseek",
"lsetxattr",
"lstat",
"lstat64",
"madvise",
"mbind",
"membarrier",
"memfd_create",
"memfd_secret",
"mincore",
"mkdir",
"mkdirat",
"mknod",
"mknodat",
"mlock",
"mlock2",
"mlockall",
"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",
"msgget",
"msgrcv",
"msgsnd",
"msync",
"munlock",
"munlockall",
"munmap",
"name_to_handle_at",
"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",
"read",
"readahead",
"readlink",
"readlinkat",
"readv",
"reboot",
"recv",
"recvfrom",
"recvmmsg",
"recvmmsg_time64",
"recvmsg",
"remap_file_pages",
"removexattr",
"rename",
"renameat",
"renameat2",
"restart_syscall",
"rmdir",
"rseq",
"rt_sigaction",
"rt_sigpending",
"rt_sigprocmask",
"rt_sigqueueinfo",
"rt_sigreturn",
"rt_sigsuspend",
"rt_sigtimedwait",
"rt_sigtimedwait_time64",
"rt_tgsigqueueinfo",
"sched_get_priority_max",
"sched_get_priority_min",
"sched_getaffinity",
"sched_getattr",
"sched_getparam",
"sched_getscheduler",
"sched_rr_get_interval",
"sched_rr_get_interval_time64",
"sched_setaffinity",
"sched_setattr",
"sched_setparam",
"sched_setscheduler",
"sched_yield",
"seccomp",
"select",
"semctl",
"semget",
"semop",
"semtimedop",
"semtimedop_time64",
"send",
"sendfile",
"sendfile64",
"sendmmsg",
"sendmsg",
"sendto",
"set_mempolicy",
"set_robust_list",
"set_thread_area",
"set_tid_address",
"setfsgid",
"setfsgid32",
"setfsuid",
"setfsuid32",
"setgid",
"setgid32",
"setgroups",
"setgroups32",
"setitimer",
"setns",
"setpgid",
"setpriority",
"setregid",
"setregid32",
"setresgid",
"setresgid32",
"setresuid",
"setresuid32",
"setreuid",
"setreuid32",
"setrlimit",
"setsid",
"setsockopt",
"setuid",
"setuid32",
"setxattr",
"shmat",
"shmctl",
"shmdt",
"shmget",
"shutdown",
"sigaltstack",
"signal",
"signalfd",
"signalfd4",
"sigprocmask",
"sigreturn",
"socketcall",
"socketpair",
"splice",
"stat",
"stat64",
"statfs",
"statfs64",
"statx",
"symlink",
"symlinkat",
"sync",
"sync_file_range",
"syncfs",
"sysinfo",
"syslog",
"tee",
"tgkill",
"time",
"timer_create",
"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",
"truncate64",
"ugetrlimit",
"umask",
"umount",
"umount2",
"uname",
"unlink",
"unlinkat",
"unshare",
"utime",
"utimensat",
"utimensat_time64",
"utimes",
"vfork",
"wait4",
"waitid",
"waitpid",
"write",
"writev",
},
Action: ActAllow,
Args: []*Arg{},
},
{
Names: []string{"personality"},
Action: ActAllow,
Args: []*Arg{
{
Index: 0,
Value: 0x0,
Op: OpEqualTo,
},
},
},
{
Names: []string{"personality"},
Action: ActAllow,
Args: []*Arg{
{
Index: 0,
Value: 0x0008,
Op: OpEqualTo,
},
},
},
{
Names: []string{"personality"},
Action: ActAllow,
Args: []*Arg{
{
Index: 0,
Value: 0x20000,
Op: OpEqualTo,
},
},
},
{
Names: []string{"personality"},
Action: ActAllow,
Args: []*Arg{
{
Index: 0,
Value: 0x20008,
Op: OpEqualTo,
},
},
},
{
Names: []string{"personality"},
Action: ActAllow,
Args: []*Arg{
{
Index: 0,
Value: 0xffffffff,
Op: OpEqualTo,
},
},
},
{
Names: []string{
"sync_file_range2",
"swapcontext",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"ppc64le"},
},
},
{
Names: []string{
"arm_fadvise64_64",
"arm_sync_file_range",
"breakpoint",
"cacheflush",
"set_tls",
"sync_file_range2",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"arm", "arm64"},
},
},
{
Names: []string{
"arch_prctl",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"amd64", "x32"},
},
},
{
Names: []string{
"modify_ldt",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"amd64", "x32", "x86"},
},
},
{
Names: []string{
"s390_pci_mmio_read",
"s390_pci_mmio_write",
"s390_runtime_instr",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"s390", "s390x"},
},
},
{
Names: []string{
"riscv_flush_icache",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"riscv64"},
},
},
{
Names: []string{
"open_by_handle_at",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
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",
"fanotify_init",
"lookup_dcookie",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_SYS_ADMIN"},
},
},
{
Names: []string{
"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{
"chroot",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
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",
"finit_module",
"init_module",
"query_module",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_SYS_MODULE"},
},
},
{
Names: []string{
"delete_module",
"finit_module",
"init_module",
"query_module",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_MODULE"},
},
},
{
Names: []string{
"acct",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
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_madvise",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_SYS_PTRACE"},
},
},
{
Names: []string{
"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{},
Includes: Filter{
Caps: []string{"CAP_SYS_RAWIO"},
},
},
{
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",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
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",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
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: unix.AF_NETLINK,
Op: OpEqualTo,
},
{
Index: 2,
Value: unix.NETLINK_AUDIT,
Op: OpEqualTo,
},
},
Excludes: Filter{
Caps: []string{"CAP_AUDIT_WRITE"},
},
},
{
Names: []string{
"socket",
},
Action: ActAllow,
Args: []*Arg{
{
Index: 2,
Value: unix.NETLINK_AUDIT,
Op: OpNotEqual,
},
},
Excludes: Filter{
Caps: []string{"CAP_AUDIT_WRITE"},
},
},
{
Names: []string{
"socket",
},
Action: ActAllow,
Args: []*Arg{
{
Index: 0,
Value: unix.AF_NETLINK,
Op: OpNotEqual,
},
},
Excludes: Filter{
Caps: []string{"CAP_AUDIT_WRITE"},
},
},
{
Names: []string{
"socket",
},
Action: ActAllow,
Args: []*Arg{
{
Index: 2,
Value: unix.NETLINK_AUDIT,
Op: OpNotEqual,
},
},
Excludes: Filter{
Caps: []string{"CAP_AUDIT_WRITE"},
},
},
{
Names: []string{
"socket",
},
Action: ActAllow,
Includes: Filter{
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,
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)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,225 @@
//go:build seccomp
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2018 Docker, Inc.
package seccomp
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"github.com/opencontainers/runtime-spec/specs-go"
libseccomp "github.com/seccomp/libseccomp-golang"
)
//go:generate go run -tags 'seccomp' generate.go
// GetDefaultProfile returns the default seccomp profile.
func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) {
return setupSeccomp(DefaultProfile(), rs)
}
// LoadProfile takes a json string and decodes the seccomp profile.
func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
var config Seccomp
if err := json.Unmarshal([]byte(body), &config); err != nil {
return nil, fmt.Errorf("decoding seccomp profile failed: %v", err)
}
return setupSeccomp(&config, rs)
}
// LoadProfileFromBytes takes a byte slice and decodes the seccomp profile.
func LoadProfileFromBytes(body []byte, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
config := &Seccomp{}
if err := json.Unmarshal(body, config); err != nil {
return nil, fmt.Errorf("decoding seccomp profile failed: %v", err)
}
return setupSeccomp(config, rs)
}
// LoadProfileFromConfig takes a Seccomp struct and a spec to retrieve a LinuxSeccomp
func LoadProfileFromConfig(config *Seccomp, specgen *specs.Spec) (*specs.LinuxSeccomp, error) {
return setupSeccomp(config, specgen)
}
var nativeToSeccomp = map[string]Arch{
"amd64": ArchX86_64,
"arm64": ArchAARCH64,
"mips64": ArchMIPS64,
"mips64n32": ArchMIPS64N32,
"mipsel64": ArchMIPSEL64,
"mipsel64n32": ArchMIPSEL64N32,
"s390x": ArchS390X,
}
// inSlice tests whether a string is contained in a slice of strings or not.
// Comparison is case sensitive
func inSlice(slice []string, s string) bool {
for _, ss := range slice {
if s == ss {
return true
}
}
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
}
// No default action specified, no syscalls listed, assume seccomp disabled
if config.DefaultAction == "" && len(config.Syscalls) == 0 {
return nil, nil
}
newConfig := &specs.LinuxSeccomp{}
var arch string
native, err := libseccomp.GetNativeArch()
if err == nil {
arch = native.String()
}
if err := getArchitectures(config, newConfig); err != nil {
return nil, err
}
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]
if ok {
if a.Arch == seccompArch {
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a.Arch))
for _, sa := range a.SubArches {
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(sa))
}
break
}
}
}
}
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 {
if len(call.Excludes.Arches) > 0 {
if inSlice(call.Excludes.Arches, arch) {
continue Loop
}
}
if len(call.Excludes.Caps) > 0 {
for _, c := range call.Excludes.Caps {
if rs != nil && rs.Process != nil && rs.Process.Capabilities != nil && inSlice(rs.Process.Capabilities.Bounding, c) {
continue Loop
}
}
}
if len(call.Includes.Arches) > 0 {
if !inSlice(call.Includes.Arches, arch) {
continue Loop
}
}
if len(call.Includes.Caps) > 0 {
for _, c := range call.Includes.Caps {
if rs != nil && rs.Process != nil && rs.Process.Capabilities != nil && !inSlice(rs.Process.Capabilities.Bounding, c) {
continue Loop
}
}
}
if call.Name != "" && len(call.Names) != 0 {
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, errno))
}
if len(call.Names) > 0 {
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, errno))
}
}
return newConfig, nil
}
func createSpecsSyscall(names []string, action Action, args []*Arg, errnoRet *uint) specs.LinuxSyscall {
newCall := specs.LinuxSyscall{
Names: names,
Action: specs.LinuxSeccompAction(action),
ErrnoRet: errnoRet,
}
// Loop through all the arguments of the syscall and convert them
for _, arg := range args {
newArg := specs.LinuxSeccompArg{
Index: arg.Index,
Value: arg.Value,
ValueTwo: arg.ValueTwo,
Op: specs.LinuxSeccompOperator(arg.Op),
}
newCall.Args = append(newCall.Args, newArg)
}
return newCall
}
// IsEnabled returns true if seccomp is enabled for the host.
func IsEnabled() bool {
return IsSupported()
}

View File

@ -0,0 +1,46 @@
//go:build !linux || !seccomp
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2018 Docker, Inc.
package seccomp
import (
"errors"
"github.com/opencontainers/runtime-spec/specs-go"
)
var errNotSupported = errors.New("seccomp not enabled in this build")
// 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 unsupported systems
func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) {
return nil, errNotSupported
}
// LoadProfileFromBytes takes a byte slice and decodes the seccomp profile.
func LoadProfileFromBytes(body []byte, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
return nil, errNotSupported
}
// LoadProfileFromConfig takes a Seccomp struct and a spec to retrieve a LinuxSeccomp
func LoadProfileFromConfig(config *Seccomp, specgen *specs.Spec) (*specs.LinuxSeccomp, error) {
return nil, errNotSupported
}
// IsEnabled returns true if seccomp is enabled for the host.
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,16 +1,28 @@
package types
package seccomp
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2018 Docker, Inc.
// 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 an specific architecture
// Architecture is used to represent a specific architecture
// and its sub-architectures
type Architecture struct {
Arch Arch `json:"architecture"`
@ -23,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"
@ -39,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
@ -46,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
@ -90,4 +115,7 @@ type Syscall struct {
Comment string `json:"comment"`
Includes Filter `json:"includes"`
Excludes Filter `json:"excludes"`
// 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
}

View File

@ -12,17 +12,11 @@ Abhijeet Kasurde <akasurde@redhat.com>
Abhinav Ajgaonkar <abhinav316@gmail.com>
Abhishek Chanda <abhishek.becs@gmail.com>
Abin Shahab <ashahab@altiscale.com>
Adam Avilla <aavilla@yp.com>
Adam Kunk <adam.kunk@tiaa-cref.org>
Adam Miller <admiller@redhat.com>
Adam Mills <adam@armills.info>
Adam Singer <financeCoding@gmail.com>
Adam Walz <adam@adamwalz.net>
Aditi Rajagopal <arajagopal@us.ibm.com>
Aditya <aditya@netroy.in>
Adolfo Ochagavía <aochagavia92@gmail.com>
Adria Casas <adriacasas88@gmail.com>
Adrian Moisey <adrian@changeover.za.net>
Adrian Mouat <adrian.mouat@gmail.com>
Adrian Oprea <adrian@codesi.nz>
Adrien Folie <folie.adrien@gmail.com>
@ -47,11 +41,9 @@ Alena Prokharchyk <alena@rancher.com>
Alessandro Boch <aboch@docker.com>
Alessio Biancalana <dottorblaster@gmail.com>
Alex Chan <alex@alexwlchan.net>
Alex Coventry <alx@empirical.com>
Alex Crawford <alex.crawford@coreos.com>
Alex Ellis <alexellis2@gmail.com>
Alex Gaynor <alex.gaynor@gmail.com>
Alex Olshansky <i@creagenics.com>
Alex Samorukov <samm@os2.kiev.ua>
Alex Warhawk <ax.warhawk@gmail.com>
Alexander Artemenko <svetlyak.40wt@gmail.com>
@ -59,7 +51,7 @@ Alexander Boyd <alex@opengroove.org>
Alexander Larsson <alexl@redhat.com>
Alexander Morozov <lk4d4@docker.com>
Alexander Shopov <ash@kambanaria.org>
Alexandre Beslic <alexandre.beslic@gmail.com>
Alexandre Beslic <abronan@docker.com>
Alexandre González <agonzalezro@gmail.com>
Alexandru Sfirlogea <alexandru.sfirlogea@gmail.com>
Alexey Guskov <lexag@mail.ru>
@ -70,13 +62,11 @@ Ali Dehghani <ali.dehghani.g@gmail.com>
Allen Madsen <blatyo@gmail.com>
Allen Sun <allen.sun@daocloud.io>
almoehi <almoehi@users.noreply.github.com>
Alvaro Saurin <alvaro.saurin@gmail.com>
Alvin Richards <alvin.richards@docker.com>
amangoel <amangoel@gmail.com>
Amen Belayneh <amenbelayneh@gmail.com>
Amit Bakshi <ambakshi@gmail.com>
Amit Krishnan <amit.krishnan@oracle.com>
Amit Shukla <amit.shukla@docker.com>
Amy Lindburg <amy.lindburg@docker.com>
Anand Patil <anand.prabhakar.patil@gmail.com>
AnandkumarPatel <anandkumarpatel@gmail.com>
@ -90,7 +80,6 @@ Andrea Turli <andrea.turli@gmail.com>
Andreas Köhler <andi5.py@gmx.net>
Andreas Savvides <andreas@editd.com>
Andreas Tiefenthaler <at@an-ti.eu>
Andrei Gherzan <andrei@resin.io>
Andrew C. Bodine <acbodine@us.ibm.com>
Andrew Clay Shafer <andrewcshafer@gmail.com>
Andrew Duckworth <grillopress@gmail.com>
@ -102,7 +91,6 @@ Andrew Macgregor <andrew.macgregor@agworld.com.au>
Andrew Macpherson <hopscotch23@gmail.com>
Andrew Martin <sublimino@gmail.com>
Andrew Munsell <andrew@wizardapps.net>
Andrew Po <absourd.noise@gmail.com>
Andrew Weiss <andrew.weiss@outlook.com>
Andrew Williams <williams.andrew@gmail.com>
Andrews Medina <andrewsmedina@gmail.com>
@ -119,7 +107,6 @@ Andy Smith <github@anarkystic.com>
Andy Wilson <wilson.andrew.j+github@gmail.com>
Anes Hasicic <anes.hasicic@gmail.com>
Anil Belur <askb23@gmail.com>
Anil Madhavapeddy <anil@recoil.org>
Ankush Agarwal <ankushagarwal11@gmail.com>
Anonmily <michelle@michelleliu.io>
Anthon van der Neut <anthon@mnt.org>
@ -131,13 +118,11 @@ Anton Nikitin <anton.k.nikitin@gmail.com>
Anton Polonskiy <anton.polonskiy@gmail.com>
Anton Tiurin <noxiouz@yandex.ru>
Antonio Murdaca <antonio.murdaca@gmail.com>
Antonis Kalipetis <akalipetis@gmail.com>
Antony Messerli <amesserl@rackspace.com>
Anuj Bahuguna <anujbahuguna.dev@gmail.com>
Anusha Ragunathan <anusha.ragunathan@docker.com>
apocas <petermdias@gmail.com>
ArikaChen <eaglesora@gmail.com>
Arnaud Lefebvre <a.lefebvre@outlook.fr>
Arnaud Porterie <arnaud.porterie@docker.com>
Arthur Barr <arthur.barr@uk.ibm.com>
Arthur Gautier <baloo@gandi.net>
@ -147,7 +132,6 @@ Asbjørn Enge <asbjorn@hanafjedle.net>
averagehuman <averagehuman@users.noreply.github.com>
Avi Das <andas222@gmail.com>
Avi Miller <avi.miller@oracle.com>
Avi Vaid <avaid1996@gmail.com>
ayoshitake <airandfingers@gmail.com>
Azat Khuyiyakhmetov <shadow_uz@mail.ru>
Bardia Keyoumarsi <bkeyouma@ucsc.edu>
@ -169,7 +153,6 @@ Bernerd Schaefer <bj.schaefer@gmail.com>
Bert Goethals <bert@bertg.be>
Bharath Thiruveedula <bharath_ves@hotmail.com>
Bhiraj Butala <abhiraj.butala@gmail.com>
Bilal Amarni <bilal.amarni@gmail.com>
Bill W <SydOps@users.noreply.github.com>
bin liu <liubin0329@users.noreply.github.com>
Blake Geno <blakegeno@gmail.com>
@ -218,9 +201,7 @@ Cameron Boehmer <cameron.boehmer@gmail.com>
Cameron Spear <cameronspear@gmail.com>
Campbell Allen <campbell.allen@gmail.com>
Candid Dauth <cdauth@cdauth.eu>
Cao Weiwei <cao.weiwei30@zte.com.cn>
Carl Henrik Lunde <chlunde@ping.uio.no>
Carl Loa Odin <carlodin@gmail.com>
Carl X. Su <bcbcarl@gmail.com>
Carlos Alexandro Becker <caarlos0@gmail.com>
Carlos Sanchez <carlos@apache.org>
@ -238,7 +219,6 @@ Charles Law <claw@conduce.com>
Charles Lindsay <chaz@chazomatic.us>
Charles Merriam <charles.merriam@gmail.com>
Charles Sarrazin <charles@sarraz.in>
Charles Smith <charles.smith@docker.com>
Charlie Lewis <charliel@lab41.org>
Chase Bolt <chase.bolt@gmail.com>
ChaYoung You <yousbe@gmail.com>
@ -290,7 +270,6 @@ Colm Hally <colmhally@gmail.com>
companycy <companycy@gmail.com>
Cory Forsyth <cory.forsyth@gmail.com>
cressie176 <github@stephen-cresswell.net>
CrimsonGlory <CrimsonGlory@users.noreply.github.com>
Cristian Staretu <cristian.staretu@gmail.com>
cristiano balducci <cristiano.balducci@gmail.com>
Cruceru Calin-Cristian <crucerucalincristian@gmail.com>
@ -299,14 +278,12 @@ Daan van Berkel <daan.v.berkel.1980@gmail.com>
Daehyeok Mun <daehyeok@gmail.com>
Dafydd Crosby <dtcrsby@gmail.com>
dalanlan <dalanlan925@gmail.com>
Damian Smyth <damian@dsau.co>
Damien Nadé <github@livna.org>
Damien Nozay <damien.nozay@gmail.com>
Damjan Georgievski <gdamjan@gmail.com>
Dan Anolik <dan@anolik.net>
Dan Buch <d.buch@modcloth.com>
Dan Cotora <dan@bluevision.ro>
Dan Feldman <danf@jfrog.com>
Dan Griffin <dgriffin@peer1.com>
Dan Hirsch <thequux@upstandinghackers.com>
Dan Keder <dan.keder@gmail.com>
@ -329,7 +306,6 @@ Daniel Nordberg <dnordberg@gmail.com>
Daniel Robinson <gottagetmac@gmail.com>
Daniel S <dan.streby@gmail.com>
Daniel Von Fange <daniel@leancoder.com>
Daniel X Moore <yahivin@gmail.com>
Daniel YC Lin <dlin.tw@gmail.com>
Daniel Zhang <jmzwcn@gmail.com>
Daniel, Dao Quang Minh <dqminh@cloudflare.com>
@ -338,9 +314,8 @@ Danny Yates <danny@codeaholics.org>
Darren Coxall <darren@darrencoxall.com>
Darren Shepherd <darren.s.shepherd@gmail.com>
Darren Stahl <darst@microsoft.com>
Davanum Srinivas <davanum@gmail.com>
Dave Barboza <dbarboza@datto.com>
Dave Henderson <dhenderson@gmail.com>
Dave Henderson <Dave.Henderson@ca.ibm.com>
Dave MacDonald <mindlapse@gmail.com>
Dave Tucker <dt@docker.com>
David Anderson <dave@natulte.net>
@ -349,12 +324,9 @@ David Corking <dmc-source@dcorking.com>
David Cramer <davcrame@cisco.com>
David Currie <david_currie@uk.ibm.com>
David Davis <daviddavis@redhat.com>
David Dooling <dooling@gmail.com>
David Gageot <david@gageot.net>
David Gebler <davidgebler@gmail.com>
David Lawrence <david.lawrence@docker.com>
David Lechner <david@lechnology.com>
David M. Karr <davidmichaelkarr@gmail.com>
David Mackey <tdmackey@booleanhaiku.com>
David Mat <david@davidmat.com>
David Mcanulty <github@hellspark.com>
@ -363,12 +335,10 @@ David R. Jenni <david.r.jenni@gmail.com>
David Röthlisberger <david@rothlis.net>
David Sheets <sheets@alum.mit.edu>
David Sissitka <me@dsissitka.com>
David Trott <github@davidtrott.com>
David Xia <dxia@spotify.com>
David Young <yangboh@cn.ibm.com>
Davide Ceretti <davide.ceretti@hogarthww.com>
Dawn Chen <dawnchen@google.com>
dbdd <wangtong2712@gmail.com>
dcylabs <dcylabs@gmail.com>
decadent <decadent@users.noreply.github.com>
deed02392 <georgehafiz@gmail.com>
@ -387,10 +357,8 @@ devmeyster <arthurfbi@yahoo.com>
Devvyn Murphy <devvyn@devvyn.com>
Dharmit Shah <shahdharmit@gmail.com>
Dieter Reuter <dieter.reuter@me.com>
Dillon Dixon <dillondixon@gmail.com>
Dima Stopel <dima@twistlock.com>
Dimitri John Ledkov <dimitri.j.ledkov@intel.com>
Dimitris Rozakis <dimrozakis@gmail.com>
Dimitry Andric <d.andric@activevideo.com>
Dinesh Subhraveti <dineshs@altiscale.com>
Diogo Monica <diogo@docker.com>
@ -398,10 +366,8 @@ DiuDiugirl <sophia.wang@pku.edu.cn>
Djibril Koné <kone.djibril@gmail.com>
dkumor <daniel@dkumor.com>
Dmitri Logvinenko <dmitri.logvinenko@gmail.com>
Dmitri Shuralyov <shurcooL@gmail.com>
Dmitry Demeshchuk <demeshchuk@gmail.com>
Dmitry Gusev <dmitry.gusev@gmail.com>
Dmitry Smirnov <onlyjob@member.fsf.org>
Dmitry V. Krivenok <krivenok.dmitry@gmail.com>
Dmitry Vorobev <dimahabr@gmail.com>
Dolph Mathews <dolph.mathews@gmail.com>
@ -413,20 +379,17 @@ Don Spaulding <donspauldingii@gmail.com>
Donald Huang <don.hcd@gmail.com>
Dong Chen <dongluo.chen@docker.com>
Donovan Jones <git@gamma.net.nz>
Doron Podoleanu <doronp@il.ibm.com>
Doug Davis <dug@us.ibm.com>
Doug MacEachern <dougm@vmware.com>
Doug Tangren <d.tangren@gmail.com>
Dr Nic Williams <drnicwilliams@gmail.com>
dragon788 <dragon788@users.noreply.github.com>
Dražen Lučanin <kermit666@gmail.com>
Drew Erny <drew.erny@docker.com>
Dustin Sallings <dustin@spy.net>
Ed Costello <epc@epcostello.com>
Edmund Wagner <edmund-wagner@web.de>
Eiichi Tsukata <devel@etsukata.com>
Eike Herzbach <eike@herzbach.net>
Eivin Giske Skaaren <eivinsn@axis.com>
Eivind Uggedal <eivind@uggedal.com>
Elan Ruusamäe <glen@delfi.ee>
Elias Probst <mail@eliasprobst.eu>
@ -439,7 +402,6 @@ Emily Rose <emily@contactvibe.com>
Emir Ozer <emirozer@yandex.com>
Enguerran <engcolson@gmail.com>
Eohyung Lee <liquidnuker@gmail.com>
Eric Barch <barch@tomesoftware.com>
Eric Hanchrow <ehanchrow@ine.com>
Eric Lee <thenorthsecedes@gmail.com>
Eric Myhre <hash@exultant.us>
@ -468,14 +430,12 @@ Evan Hazlett <ejhazlett@gmail.com>
Evan Krall <krall@yelp.com>
Evan Phoenix <evan@fallingsnow.net>
Evan Wies <evan@neomantra.net>
Everett Toews <everett.toews@rackspace.com>
Evgeny Vereshchagin <evvers@ya.ru>
Ewa Czechowska <ewa@ai-traders.com>
Eystein Måløy Stenberg <eystein.maloy.stenberg@cfengine.com>
ezbercih <cem.ezberci@gmail.com>
Fabiano Rosas <farosas@br.ibm.com>
Fabio Falci <fabiofalci@gmail.com>
Fabio Rapposelli <fabio@vmware.com>
Fabio Rehm <fgrehm@gmail.com>
Fabrizio Regini <freegenie@gmail.com>
Fabrizio Soppelsa <fsoppelsa@mirantis.com>
@ -488,12 +448,10 @@ Federico Gimenez <fgimenez@coit.es>
Felix Geisendörfer <felix@debuggable.com>
Felix Hupfeld <quofelix@users.noreply.github.com>
Felix Rabe <felix@rabe.io>
Felix Ruess <felix.ruess@roboception.de>
Felix Schindler <fschindler@weluse.de>
Ferenc Szabo <pragmaticfrank@gmail.com>
Fernando <fermayo@gmail.com>
Fero Volar <alian@alian.info>
Ferran Rodenas <frodenas@gmail.com>
Filipe Brandenburger <filbranden@google.com>
Filipe Oliveira <contato@fmoliveira.com.br>
fl0yd <fl0yd@me.com>
@ -504,10 +462,7 @@ Florian Klein <florian.klein@free.fr>
Florian Maier <marsmensch@users.noreply.github.com>
Florian Weingarten <flo@hackvalue.de>
Florin Asavoaie <florin.asavoaie@gmail.com>
fonglh <fonglh@gmail.com>
fortinux <fortinux@users.noreply.github.com>
Francesc Campoy <campoy@google.com>
Francis Chuang <francis.chuang@boostport.com>
Francisco Carriedo <fcarriedo@gmail.com>
Francisco Souza <f@souza.cc>
Frank Groeneveld <frank@ivaldi.nl>
@ -519,7 +474,6 @@ Frederick F. Kautz IV <fkautz@redhat.com>
Frederik Loeffert <frederik@zitrusmedia.de>
Frederik Nordahl Jul Sabroe <frederikns@gmail.com>
Freek Kalter <freek@kalteronline.org>
frosforever <frosforever@users.noreply.github.com>
fy2462 <fy2462@gmail.com>
Félix Baylac-Jacqué <baylac.felix@gmail.com>
Félix Cantournet <felix.cantournet@cloudwatt.com>
@ -575,7 +529,7 @@ Hao Zhang <21521210@zju.edu.cn>
Harald Albers <github@albersweb.de>
Harley Laue <losinggeneration@gmail.com>
Harold Cooper <hrldcpr@gmail.com>
Harry Zhang <harryz@hyper.sh>
Harry Zhang <harryzhang@zju.edu.cn>
He Simei <hesimei@zju.edu.cn>
heartlock <21521209@zju.edu.cn>
Hector Castro <hectcastro@gmail.com>
@ -647,7 +601,6 @@ Jan Toebes <jan@toebes.info>
Jan-Gerd Tenberge <janten@gmail.com>
Jan-Jaap Driessen <janjaapdriessen@gmail.com>
Jana Radhakrishnan <mrjana@docker.com>
Jannick Fahlbusch <git@jf-projects.de>
Januar Wayong <januar@gmail.com>
Jared Biel <jared.biel@bolderthinking.com>
Jared Hocutt <jaredh@netapp.com>
@ -680,7 +633,6 @@ Jeff Lindsay <progrium@gmail.com>
Jeff Mickey <j@codemac.net>
Jeff Minard <jeff@creditkarma.com>
Jeff Nickoloff <jeff.nickoloff@gmail.com>
Jeff Silberman <jsilberm@gmail.com>
Jeff Welch <whatthejeff@gmail.com>
Jeffrey Bolle <jeffreybolle@gmail.com>
Jeffrey Morgan <jmorganca@gmail.com>
@ -693,11 +645,10 @@ Jeremy Unruh <jeremybunruh@gmail.com>
Jeroen Jacobs <github@jeroenj.be>
Jesse Dearing <jesse.dearing@gmail.com>
Jesse Dubay <jesse@thefortytwo.net>
Jessica Frazelle <jessfraz@google.com>
Jessica Frazelle <jess@mesosphere.com>
Jezeniel Zapanta <jpzapanta22@gmail.com>
jgeiger <jgeiger@gmail.com>
Jhon Honce <jhonce@redhat.com>
Ji.Zhilong <zhilongji@gmail.com>
Jian Zhang <zhangjian.fnst@cn.fujitsu.com>
jianbosun <wonderflow.sun@gmail.com>
Jilles Oldenbeuving <ojilles@gmail.com>
@ -711,7 +662,6 @@ Jiri Popelka <jpopelka@redhat.com>
Jiří Župka <jzupka@redhat.com>
jjy <jiangjinyang@outlook.com>
jmzwcn <jmzwcn@gmail.com>
Joao Fernandes <joao.fernandes@docker.com>
Joe Beda <joe.github@bedafamily.com>
Joe Doliner <jdoliner@pachyderm.io>
Joe Ferguson <joe@infosiftr.com>
@ -727,7 +677,6 @@ Joey Gibson <joey@joeygibson.com>
Joffrey F <joffrey@docker.com>
Johan Euphrosine <proppy@google.com>
Johan Rydberg <johan.rydberg@gmail.com>
Johanan Lieberman <johanan.lieberman@gmail.com>
Johannes 'fish' Ziemke <github@freigeist.org>
John Costa <john.costa@gmail.com>
John Feminella <jxf@jxf.me>
@ -739,7 +688,6 @@ John Starks <jostarks@microsoft.com>
John Tims <john.k.tims@gmail.com>
John Warwick <jwarwick@gmail.com>
John Willis <john.willis@docker.com>
johnharris85 <john@johnharris.io>
Jon Wedaman <jweede@gmail.com>
Jonas Pfenniger <jonas@pfenniger.name>
Jonathan A. Sternberg <jonathansternberg@gmail.com>
@ -747,25 +695,21 @@ Jonathan Boulle <jonathanboulle@gmail.com>
Jonathan Camp <jonathan@irondojo.com>
Jonathan Dowland <jon+github@alcopop.org>
Jonathan Lebon <jlebon@redhat.com>
Jonathan Lomas <jonathan@floatinglomas.ca>
Jonathan McCrohan <jmccrohan@gmail.com>
Jonathan Mueller <j.mueller@apoveda.ch>
Jonathan Pares <jonathanpa@users.noreply.github.com>
Jonathan Rudenberg <jonathan@titanous.com>
Jonathan Stoppani <jonathan.stoppani@divio.com>
Joost Cassee <joost@cassee.net>
Jordan <jjn2009@users.noreply.github.com>
Jordan Arentsen <blissdev@gmail.com>
Jordan Sissel <jls@semicomplete.com>
Jordan Williams <jordan@jwillikers.com>
Jose Diaz-Gonzalez <josegonzalez@users.noreply.github.com>
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
Joseph Hager <ajhager@gmail.com>
Joseph Kern <jkern@semafour.net>
Josh <jokajak@gmail.com>
Josh Bodah <jb3689@yahoo.com>
Josh Chorlton <jchorlton@gmail.com>
Josh Hawn <josh.hawn@docker.com>
Josh Horwitz <horwitz@addthis.com>
Josh Poimboeuf <jpoimboe@redhat.com>
Josiah Kiehl <jkiehl@riotgames.com>
José Tomás Albornoz <jojo@eljojo.net>
@ -786,16 +730,14 @@ Justin Force <justin.force@gmail.com>
Justin Plock <jplock@users.noreply.github.com>
Justin Simonelis <justin.p.simonelis@gmail.com>
Justin Terry <juterry@microsoft.com>
Justyn Temme <justyntemme@gmail.com>
Jyrki Puttonen <jyrkiput@gmail.com>
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
Jörg Thalheim <joerg@higgsboson.tk>
Kai Blin <kai@samba.org>
Kai Qiang Wu(Kennan) <wkq5325@gmail.com>
Kai Qiang Wu(Kennan) <wkqwu@cn.ibm.com>
Kamil Domański <kamil@domanski.co>
kamjar gerami <kami.gerami@gmail.com>
Kanstantsin Shautsou <kanstantsin.sha@gmail.com>
Kara Alexandra <kalexandra@us.ibm.com>
Karan Lyons <karan@karanlyons.com>
Kareem Khazem <karkhaz@karkhaz.com>
kargakis <kargakis@users.noreply.github.com>
@ -807,22 +749,17 @@ Katrina Owen <katrina.owen@gmail.com>
Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
kayrus <kay.diam@gmail.com>
Ke Xu <leonhartx.k@gmail.com>
Keith Hudgins <greenman@greenman.org>
Keli Hu <dev@keli.hu>
Ken Cochrane <kencochrane@gmail.com>
Ken Herner <kherner@progress.com>
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
Kenjiro Nakayama <nakayamakenjiro@gmail.com>
Kent Johnson <kentoj@gmail.com>
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
Kevin Burke <kev@inburke.com>
Kevin Clark <kevin.clark@gmail.com>
Kevin J. Lynagh <kevin@keminglabs.com>
Kevin Jing Qiu <kevin@idempotent.ca>
Kevin Menard <kevin@nirvdrum.com>
Kevin P. Kucharczyk <kevinkucharczyk@gmail.com>
Kevin Richardson <kevin@kevinrichardson.co>
Kevin Shi <kshi@andrew.cmu.edu>
Kevin Wallace <kevin@pentabarf.net>
Kevin Yap <me@kevinyap.ca>
@ -834,23 +771,19 @@ Kim Eik <kim@heldig.org>
Kimbro Staken <kstaken@kstaken.com>
Kir Kolyshkin <kir@openvz.org>
Kiran Gangadharan <kiran.daredevil@gmail.com>
Kirill Kolyshkin <kolyshkin@users.noreply.github.com>
Kirill SIbirev <l0kix2@gmail.com>
knappe <tyler.knappe@gmail.com>
Kohei Tsuruta <coheyxyz@gmail.com>
Koichi Shiraishi <k@zchee.io>
Konrad Kleine <konrad.wilhelm.kleine@gmail.com>
Konstantin L <sw.double@gmail.com>
Konstantin Pelykh <kpelykh@zettaset.com>
Krasimir Georgiev <support@vip-consult.co.uk>
Kris-Mikael Krister <krismikael@protonmail.com>
Kristian Haugene <kristian.haugene@capgemini.com>
Kristina Zabunova <triara.xiii@gmail.com>
krrg <krrgithub@gmail.com>
Kun Zhang <zkazure@gmail.com>
Kunal Kushwaha <kunal.kushwaha@gmail.com>
Kyle Conroy <kyle.j.conroy@gmail.com>
Kyle Linden <linden.kyle@gmail.com>
kyu <leehk1227@gmail.com>
Lachlan Coote <lcoote@vmware.com>
Lai Jiangshan <jiangshanlai@gmail.com>
@ -867,7 +800,6 @@ Laszlo Meszaros <lacienator@gmail.com>
Laurent Erignoux <lerignoux@gmail.com>
Laurie Voss <github@seldo.com>
Leandro Siqueira <leandro.siqueira@gmail.com>
Lee Chao <932819864@qq.com>
Lee, Meng-Han <sunrisedm4@gmail.com>
leeplay <hyeongkyu.lee@navercorp.com>
Lei Jitang <leijitang@huawei.com>
@ -878,7 +810,6 @@ Levi Blackstone <levi.blackstone@rackspace.com>
Levi Gross <levi@levigross.com>
Lewis Marshall <lewis@lmars.net>
Lewis Peckover <lew+github@lew.io>
Liam Macgillavry <liam@kumina.nl>
Liana Lo <liana.lixia@gmail.com>
Liang Mingqiang <mqliang.zju@gmail.com>
Liang-Chi Hsieh <viirya@gmail.com>
@ -891,7 +822,6 @@ Liran Tal <liran.tal@gmail.com>
Liron Levin <liron@twistlock.com>
Liu Bo <bo.li.liu@oracle.com>
Liu Hua <sdu.liu@huawei.com>
lixiaobing10051267 <li.xiaobing1@zte.com.cn>
LIZAO LI <lzlarryli@gmail.com>
Lloyd Dewolf <foolswisdom@gmail.com>
Lokesh Mandvekar <lsm5@fedoraproject.org>
@ -903,8 +833,6 @@ Luca Marturana <lucamarturana@gmail.com>
Luca Orlandi <luca.orlandi@gmail.com>
Luca-Bogdan Grigorescu <Luca-Bogdan Grigorescu>
Lucas Chan <lucas-github@lucaschan.com>
Lucas Chi <lucas@teacherspayteachers.com>
Luciano Mores <leslau@gmail.com>
Luis Martínez de Bartolomé Izquierdo <lmartinez@biicode.com>
Lukas Waslowski <cr7pt0gr4ph7@gmail.com>
lukaspustina <lukas.pustina@centerdevice.com>
@ -923,7 +851,6 @@ Malte Janduda <mail@janduda.net>
manchoz <giampaolo@trampolineup.com>
Manfred Touron <m@42.am>
Manfred Zabarauskas <manfredas@zabarauskas.com>
Mansi Nahar <mmn4185@rit.edu>
mansinahar <mansinahar@users.noreply.github.com>
Manuel Meurer <manuel@krautcomputing.com>
Manuel Woelker <github@manuel.woelker.org>
@ -966,10 +893,8 @@ Matt Apperson <me@mattapperson.com>
Matt Bachmann <bachmann.matt@gmail.com>
Matt Bentley <matt.bentley@docker.com>
Matt Haggard <haggardii@gmail.com>
Matt Hoyle <matt@deployable.co>
Matt McCormick <matt.mccormick@kitware.com>
Matt Moore <mattmoor@google.com>
Matt Richardson <matt@redgumtech.com.au>
Matt Robenolt <matt@ydekproductions.com>
Matthew Heon <mheon@redhat.com>
Matthew Mayer <matthewkmayer@gmail.com>
@ -981,10 +906,9 @@ Matthias Rampke <mr@soundcloud.com>
Matthieu Hauglustaine <matt.hauglustaine@gmail.com>
mattymo <raytrac3r@gmail.com>
mattyw <mattyw@me.com>
Mauricio Garavaglia <mauricio@medallia.com>
Mauricio Garavaglia <mauriciogaravaglia@gmail.com>
mauriyouth <mauriyouth@gmail.com>
Max Shytikov <mshytikov@gmail.com>
Maxim Fedchyshyn <sevmax@gmail.com>
Maxim Ivanov <ivanov.maxim@gmail.com>
Maxim Kulkin <mkulkin@mirantis.com>
Maxim Treskin <zerthurd@gmail.com>
@ -993,10 +917,8 @@ Meaglith Ma <genedna@gmail.com>
meejah <meejah@meejah.ca>
Megan Kostick <mkostick@us.ibm.com>
Mehul Kar <mehul.kar@gmail.com>
Mei ChunTao <mei.chuntao@zte.com.cn>
Mengdi Gao <usrgdd@gmail.com>
Mert Yazıcıoğlu <merty@users.noreply.github.com>
mgniu <mgniu@dataman-inc.com>
Micah Zoltu <micah@newrelic.com>
Michael A. Smith <michael@smith-li.com>
Michael Bridgen <mikeb@squaremobius.net>
@ -1022,11 +944,9 @@ Michal Fojtik <mfojtik@redhat.com>
Michal Gebauer <mishak@mishak.net>
Michal Jemala <michal.jemala@gmail.com>
Michal Minar <miminar@redhat.com>
Michal Wieczorek <wieczorek-michal@wp.pl>
Michaël Pailloncy <mpapo.dev@gmail.com>
Michał Czeraszkiewicz <czerasz@gmail.com>
Michiel@unhosted <michiel@unhosted.org>
Mickaël FORTUNATO <morsi.morsicus@gmail.com>
Miguel Angel Fernández <elmendalerenda@gmail.com>
Miguel Morales <mimoralea@gmail.com>
Mihai Borobocea <MihaiBorob@gmail.com>
@ -1037,7 +957,7 @@ Mike Danese <mikedanese@google.com>
Mike Dillon <mike@embody.org>
Mike Dougherty <mike.dougherty@docker.com>
Mike Gaffney <mike@uberu.com>
Mike Goelzer <mike.goelzer@docker.com>
Mike Goelzer <mgoelzer@docker.com>
Mike Leone <mleone896@gmail.com>
Mike MacCana <mike.maccana@gmail.com>
Mike Naberezny <mike@naberezny.com>
@ -1047,7 +967,6 @@ Mikhail Sobolev <mss@mawhrin.net>
Miloslav Trmač <mitr@redhat.com>
mingqing <limingqing@cyou-inc.com>
Mingzhen Feng <fmzhen@zju.edu.cn>
Misty Stanley-Jones <misty@docker.com>
Mitch Capper <mitch.capper@gmail.com>
mlarcher <github@ringabell.org>
Mohammad Banikazemi <mb@us.ibm.com>
@ -1084,20 +1003,15 @@ Nathan LeClaire <nathan.leclaire@docker.com>
Nathan McCauley <nathan.mccauley@docker.com>
Nathan Williams <nathan@teamtreehouse.com>
Neal McBurnett <neal@mcburnett.org>
Neil Peterson <neilpeterson@outlook.com>
Nelson Chen <crazysim@gmail.com>
Neyazul Haque <nuhaque@gmail.com>
Nghia Tran <nghia@google.com>
Niall O'Higgins <niallo@unworkable.org>
Nicholas E. Rabenau <nerab@gmx.at>
nick <nicholasjamesrusso@gmail.com>
Nick DeCoursin <n.decoursin@foodpanda.com>
Nick Irvine <nfirvine@nfirvine.com>
Nick Parker <nikaios@gmail.com>
Nick Payne <nick@kurai.co.uk>
Nick Stenning <nick.stenning@digital.cabinet-office.gov.uk>
Nick Stinemates <nick@stinemates.org>
Nicola Kabar <nicolaka@gmail.com>
Nicolas Borboën <ponsfrilus@users.noreply.github.com>
Nicolas De loof <nicolas.deloof@gmail.com>
Nicolas Dudebout <nicolas.dudebout@gatech.edu>
@ -1122,14 +1036,11 @@ odk- <github@odkurzacz.org>
Oguz Bilgic <fisyonet@gmail.com>
Oh Jinkyun <tintypemolly@gmail.com>
Ohad Schneider <ohadschn@users.noreply.github.com>
ohmystack <jun.jiang02@ele.me>
Ole Reifschneider <mail@ole-reifschneider.de>
Oliver Neal <ItsVeryWindy@users.noreply.github.com>
Olivier Gambier <dmp42@users.noreply.github.com>
Olle Jonsson <olle.jonsson@gmail.com>
Oriol Francès <oriolfa@gmail.com>
orkaa <orkica@gmail.com>
Oskar Niburski <oskarniburski@gmail.com>
Otto Kekäläinen <otto@seravo.fi>
oyld <oyld0210@163.com>
ozlerhakan <hakan.ozler@kodcu.com>
@ -1139,7 +1050,6 @@ panticz <mail@konczalski.de>
Paolo G. Giarrusso <p.giarrusso@gmail.com>
Pascal Borreli <pascal@borreli.com>
Pascal Hartig <phartig@rdrei.net>
Patrick Böänziger <patrick.baenziger@bsi-software.com>
Patrick Devine <patrick.devine@docker.com>
Patrick Hemmer <patrick.hemmer@gmail.com>
Patrick Stapleton <github@gdi2290.com>
@ -1149,7 +1059,6 @@ paul <paul@inkling.com>
Paul Annesley <paul@annesley.cc>
Paul Bellamy <paul.a.bellamy@gmail.com>
Paul Bowsher <pbowsher@globalpersonals.co.uk>
Paul Furtado <pfurtado@hubspot.com>
Paul Hammond <paul@paulhammond.org>
Paul Jimenez <pj@place.org>
Paul Lietar <paul@lietar.net>
@ -1157,19 +1066,16 @@ Paul Liljenberg <liljenberg.paul@gmail.com>
Paul Morie <pmorie@gmail.com>
Paul Nasrat <pnasrat@gmail.com>
Paul Weaver <pauweave@cisco.com>
Paulo Ribeiro <paigr.io@gmail.com>
Pavel Lobashov <ShockwaveNN@gmail.com>
Pavel Pospisil <pospispa@gmail.com>
Pavel Sutyrin <pavel.sutyrin@gmail.com>
Pavel Tikhomirov <ptikhomirov@parallels.com>
Pavlos Ratis <dastergon@gentoo.org>
Pavol Vargovcik <pallly.vargovcik@gmail.com>
Peeyush Gupta <gpeeyush@linux.vnet.ibm.com>
Peggy Li <peggyli.224@gmail.com>
Pei Su <sillyousu@gmail.com>
Penghan Wang <ph.wang@daocloud.io>
perhapszzy@sina.com <perhapszzy@sina.com>
pestophagous <pestophagous@users.noreply.github.com>
Peter Bourgon <peter@bourgon.org>
Peter Braden <peterbraden@peterbraden.co.uk>
Peter Choi <reikani@Peters-MacBook-Pro.local>
@ -1181,7 +1087,6 @@ Peter Malmgren <ptmalmgren@gmail.com>
Peter Salvatore <peter@psftw.com>
Peter Volpe <petervo@redhat.com>
Peter Waller <p@pwaller.net>
Petr Švihlík <svihlik.petr@gmail.com>
Phil <underscorephil@gmail.com>
Phil Estes <estesp@linux.vnet.ibm.com>
Phil Spitler <pspitler@gmail.com>
@ -1193,7 +1098,6 @@ pidster <pid@pidster.com>
Piergiuliano Bossi <pgbossi@gmail.com>
Pierre <py@poujade.org>
Pierre Carrier <pierre@meteor.com>
Pierre Dal-Pra <dalpra.pierre@gmail.com>
Pierre Wacrenier <pierre.wacrenier@gmail.com>
Pierre-Alain RIVIERE <pariviere@ippon.fr>
Piotr Bogdan <ppbogdan@gmail.com>
@ -1219,11 +1123,9 @@ Raghavendra K T <raghavendra.kt@linux.vnet.ibm.com>
Raghuram Devarakonda <draghuram@gmail.com>
Rajat Pandit <rp@rajatpandit.com>
Rajdeep Dua <dua_rajdeep@yahoo.com>
Ralf Sippl <ralf.sippl@gmail.com>
Ralle <spam@rasmusa.net>
Ralph Bean <rbean@redhat.com>
Ramkumar Ramachandra <artagnon@gmail.com>
Ramon Brooker <rbrooker@aetherealmind.com>
Ramon van Alteren <ramon@vanalteren.nl>
Ray Tsang <saturnism@users.noreply.github.com>
ReadmeCritic <frankensteinbot@gmail.com>
@ -1234,12 +1136,10 @@ Renato Riccieri Santos Zannon <renato.riccieri@gmail.com>
resouer <resouer@163.com>
rgstephens <greg@udon.org>
Rhys Hiltner <rhys@twitch.tv>
Rich Moyse <rich@moyse.us>
Rich Seymour <rseymour@gmail.com>
Richard <richard.scothern@gmail.com>
Richard Burnison <rburnison@ebay.com>
Richard Harvey <richard@squarecows.com>
Richard Mathie <richard.mathie@amey.co.uk>
Richard Metzler <richard@paadee.com>
Richard Scothern <richard.scothern@gmail.com>
Richo Healey <richo@psych0tik.net>
@ -1256,7 +1156,6 @@ Robert Bachmann <rb@robertbachmann.at>
Robert Bittle <guywithnose@gmail.com>
Robert Obryk <robryk@gmail.com>
Robert Stern <lexandro2000@gmail.com>
Robert Terhaar <robbyt@users.noreply.github.com>
Robert Wallis <smilingrob@gmail.com>
Roberto G. Hashioka <roberto.hashioka@docker.com>
Robin Naundorf <r.naundorf@fh-muenster.de>
@ -1275,10 +1174,8 @@ Roland Moriz <rmoriz@users.noreply.github.com>
Roma Sokolov <sokolov.r.v@gmail.com>
Roman Strashkin <roman.strashkin@gmail.com>
Ron Smits <ron.smits@gmail.com>
Ron Williams <ron.a.williams@gmail.com>
root <docker-dummy@example.com>
root <root@localhost>
root <root@lxdebmas.marist.edu>
root <root@ubuntu-14.04-amd64-vbox>
root <root@webm215.cluster016.ha.ovh.net>
Rory Hunter <roryhunter2@gmail.com>
@ -1289,7 +1186,6 @@ Rozhnov Alexandr <nox73@ya.ru>
rsmoorthy <rsmoorthy@users.noreply.github.com>
Rudolph Gottesheim <r.gottesheim@loot.at>
Rui Lopes <rgl@ruilopes.com>
Runshen Zhu <runshen.zhu@gmail.com>
Ryan Anderson <anderson.ryanc@gmail.com>
Ryan Aslett <github@mixologic.com>
Ryan Belgrave <rmb1993@gmail.com>
@ -1309,7 +1205,6 @@ Sabin Basyal <sabin.basyal@gmail.com>
Sachin Joshi <sachin_jayant_joshi@hotmail.com>
Sagar Hani <sagarhani33@gmail.com>
Sainath Grandhi <sainath.grandhi@intel.com>
sakeven <jc5930@sina.cn>
Sally O'Malley <somalley@redhat.com>
Sam Abed <sam.abed@gmail.com>
Sam Alba <sam.alba@gmail.com>
@ -1331,7 +1226,6 @@ sapphiredev <se.imas.kr@gmail.com>
Satnam Singh <satnam@raintown.org>
satoru <satorulogic@gmail.com>
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
Satoshi Tagomori <tagomoris@gmail.com>
scaleoutsean <scaleoutsean@users.noreply.github.com>
Scott Bessler <scottbessler@gmail.com>
Scott Collier <emailscottcollier@gmail.com>
@ -1352,25 +1246,19 @@ Seongyeol Lim <seongyeol37@gmail.com>
Serge Hallyn <serge.hallyn@ubuntu.com>
Sergey Alekseev <sergey.alekseev.minsk@gmail.com>
Sergey Evstifeev <sergey.evstifeev@gmail.com>
Serhat Gülçiçek <serhat25@gmail.com>
Sevki Hasirci <s@sevki.org>
Shane Canon <scanon@lbl.gov>
Shane da Silva <shane@dasilva.io>
shaunol <shaunol@gmail.com>
Shawn Landden <shawn@churchofgit.com>
Shawn Siefkas <shawn.siefkas@meredith.com>
shawnhe <shawnhe@shawnhedeMacBook-Pro.local>
Shekhar Gulati <shekhargulati84@gmail.com>
Sheng Yang <sheng@yasker.org>
Shengbo Song <thomassong@tencent.com>
Shev Yan <yandong_8212@163.com>
Shih-Yuan Lee <fourdollars@gmail.com>
Shijiang Wei <mountkin@gmail.com>
Shishir Mahajan <shishir.mahajan@redhat.com>
Shoubhik Bose <sbose78@gmail.com>
Shourya Sarcar <shourya.sarcar@gmail.com>
shuai-z <zs.broccoli@gmail.com>
Shukui Yang <yangshukui@huawei.com>
Shuwei Hao <haosw@cn.ibm.com>
Sian Lerk Lau <kiawin@gmail.com>
sidharthamani <sid@rancher.com>
@ -1381,7 +1269,6 @@ Simon Leinen <simon.leinen@gmail.com>
Simon Taranto <simon.taranto@gmail.com>
Sindhu S <sindhus@live.in>
Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
skaasten <shaunk@gmail.com>
Solganik Alexander <solganik@gmail.com>
Solomon Hykes <solomon@docker.com>
Song Gao <song@gao.io>
@ -1402,13 +1289,11 @@ Stefan Staudenmeyer <doerte@instana.com>
Stefan Weil <sw@weilnetz.de>
Stephen Crosby <stevecrozz@gmail.com>
Stephen Day <stephen.day@docker.com>
Stephen Drake <stephen@xenolith.net>
Stephen Rust <srust@blockbridge.com>
Steve Durrheimer <s.durrheimer@gmail.com>
Steve Francia <steve.francia@gmail.com>
Steve Koch <stevekochscience@gmail.com>
Steven Burgess <steven.a.burgess@hotmail.com>
Steven Erenst <stevenerenst@gmail.com>
Steven Iveson <sjiveson@outlook.com>
Steven Merrill <steven.merrill@gmail.com>
Steven Richards <steven@axiomzen.co>
@ -1423,7 +1308,6 @@ Sylvain Bellemare <sylvain@ascribe.io>
Sébastien <sebastien@yoozio.com>
Sébastien Luttringer <seblu@seblu.net>
Sébastien Stormacq <sebsto@users.noreply.github.com>
Tadej Janež <tadej.j@nez.si>
TAGOMORI Satoshi <tagomoris@gmail.com>
tang0th <tang0th@gmx.com>
Tangi COLIN <tangicolin@gmail.com>
@ -1456,9 +1340,7 @@ Thomas Swift <tgs242@gmail.com>
Thomas Tanaka <thomas.tanaka@oracle.com>
Thomas Texier <sharkone@en-mousse.org>
Tianon Gravi <admwiggin@gmail.com>
Tianyi Wang <capkurmagati@gmail.com>
Tibor Vass <teabee89@gmail.com>
Tiffany Jernigan <tiffany.f.j@gmail.com>
Tiffany Low <tiffany@box.com>
Tim Bosse <taim@bosboot.org>
Tim Dettrick <t.dettrick@uq.edu.au>
@ -1470,7 +1352,6 @@ Tim Terhorst <mynamewastaken+git@gmail.com>
Tim Wang <timwangdev@gmail.com>
Tim Waugh <twaugh@redhat.com>
Tim Wraight <tim.wraight@tangentlabs.co.uk>
timfeirg <kkcocogogo@gmail.com>
Timothy Hobbs <timothyhobbs@seznam.cz>
tjwebb123 <tjwebb123@users.noreply.github.com>
tobe <tobegit3hub@gmail.com>
@ -1478,7 +1359,6 @@ Tobias Bieniek <Tobias.Bieniek@gmx.de>
Tobias Bradtke <webwurst@gmail.com>
Tobias Gesellchen <tobias@gesellix.de>
Tobias Klauser <tklauser@distanz.ch>
Tobias Munk <schmunk@usrbin.de>
Tobias Schmidt <ts@soundcloud.com>
Tobias Schwab <tobias.schwab@dynport.de>
Todd Crane <todd@toddcrane.com>
@ -1523,7 +1403,6 @@ vagrant <vagrant@ubuntu-14.04-amd64-vbox>
Vaidas Jablonskis <jablonskis@gmail.com>
Veres Lajos <vlajos@gmail.com>
vgeta <gopikannan.venugopalsamy@gmail.com>
Victor Algaze <valgaze@gmail.com>
Victor Coisne <victor.coisne@dotcloud.com>
Victor Costan <costan@gmail.com>
Victor I. Wood <viw@t2am.com>
@ -1552,7 +1431,6 @@ Vivek Dasgupta <vdasgupt@redhat.com>
Vivek Goyal <vgoyal@redhat.com>
Vladimir Bulyga <xx@ccxx.cc>
Vladimir Kirillov <proger@wilab.org.ua>
Vladimir Pouzanov <farcaller@google.com>
Vladimir Rutsky <altsysrq@gmail.com>
Vladimir Varankin <nek.narqo+git@gmail.com>
VladimirAus <v_roudakov@yahoo.com>
@ -1570,7 +1448,6 @@ weiyan <weiyan3@huawei.com>
Weiyang Zhu <cnresonant@gmail.com>
Wen Cheng Ma <wenchma@cn.ibm.com>
Wendel Fleming <wfleming@usc.edu>
Wenkai Yin <yinw@vmware.com>
Wenxuan Zhao <viz@linux.com>
Wenyu You <21551128@zju.edu.cn>
Wes Morgan <cap10morgan@gmail.com>
@ -1587,9 +1464,7 @@ WiseTrem <shepelyov.g@gmail.com>
wlan0 <sidharthamn@gmail.com>
Wolfgang Powisch <powo@powo.priv.at>
wonderflow <wonderflow.sun@gmail.com>
Wonjun Kim <wonjun.kim@navercorp.com>
xamyzhao <x.amy.zhao@gmail.com>
Xianlu Bird <xianlubird@gmail.com>
XiaoBing Jiang <s7v7nislands@gmail.com>
Xiaoxu Chen <chenxiaoxu14@otcaix.iscas.ac.cn>
xiekeyang <xiekeyang@huawei.com>
@ -1602,7 +1477,6 @@ YAMADA Tsuyoshi <tyamada@minimum2scp.org>
Yan Feng <yanfeng2@huawei.com>
Yang Bai <hamo.by@gmail.com>
yangshukui <yangshukui@huawei.com>
Yanqiang Miao <miao.yanqiang@zte.com.cn>
Yasunori Mahata <nori@mahata.net>
Yestin Sun <sunyi0804@gmail.com>
Yi EungJun <eungjun.yi@navercorp.com>
@ -1617,8 +1491,6 @@ Youcef YEKHLEF <yyekhlef@gmail.com>
Yuan Sun <sunyuan3@huawei.com>
yuchangchun <yuchangchun1@huawei.com>
yuchengxia <yuchengxia@huawei.com>
yuexiao-wang <wang.yuexiao@zte.com.cn>
YuPengZTE <yu.peng36@zte.com.cn>
Yurii Rashkovskii <yrashk@gmail.com>
yuzou <zouyu7@huawei.com>
Zac Dover <zdover@redhat.com>
@ -1633,7 +1505,6 @@ Zhang Kun <zkazure@gmail.com>
Zhang Wei <zhangwei555@huawei.com>
Zhang Wentao <zhangwentao234@huawei.com>
Zhenan Ye <21551168@zju.edu.cn>
zhouhao <zhouhao@cn.fujitsu.com>
Zhu Guihua <zhugh.fnst@cn.fujitsu.com>
Zhuoyun Wei <wzyboy@wzyboy.org>
Zilin Du <zilin.du@gmail.com>

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
}

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