added intial commmit
This commit is contained in:
commit
1e7713c8a3
|
@ -0,0 +1,4 @@
|
|||
*
|
||||
!CMakeLists.txt
|
||||
!setcap.c
|
||||
!config.h.in
|
|
@ -0,0 +1,2 @@
|
|||
/.vscode
|
||||
/build
|
|
@ -0,0 +1,36 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
if(POLICY CMP0056)
|
||||
cmake_policy(SET CMP0056 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0065)
|
||||
cmake_policy(SET CMP0065 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0066)
|
||||
cmake_policy(SET CMP0066 NEW)
|
||||
endif()
|
||||
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-static")
|
||||
set(CMAKE_ENABLE_EXPORTS Off)
|
||||
|
||||
include(CheckFunctionExists)
|
||||
|
||||
project(setcap-static VERSION 1.0.0 LANGUAGES C)
|
||||
|
||||
find_path(NAMES sys/capability.h REQUIRED)
|
||||
find_library(LIBCAP_LIBRARY NAMES "${CMAKE_STATIC_LIBRARY_PREFIX}cap${CMAKE_STATIC_LIBRARY_SUFFIX}" cap)
|
||||
if(NOT LIBCAP_LIBRARY)
|
||||
message(FATAL_ERROR "Unable to find libcap")
|
||||
endif()
|
||||
add_library(libcap STATIC IMPORTED)
|
||||
set_target_properties(libcap PROPERTIES IMPORTED_LOCATION ${LIBCAP_LIBRARY})
|
||||
|
||||
set(CMAKE_REQUIRED_LIBRARIES "${LIBCAP_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}")
|
||||
check_function_exists(cap_set_nsowner HAVE_CAP_SET_NSOWNER)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
add_executable(setcap-static setcap.c)
|
||||
target_link_libraries(setcap-static libcap)
|
||||
target_include_directories(setcap-static PUBLIC ${PROJECT_BINARY_DIR})
|
|
@ -0,0 +1,11 @@
|
|||
FROM harbor.repository.lb.home.dc.internal.amuz.es/infrastructure/alpine-base:3.19-latest AS build
|
||||
RUN apk add --no-cache cmake make musl-dev gcc libcap-static libcap-dev
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
RUN \
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=MinSizeRel && \
|
||||
cmake --build build --config MinSizeRel && \
|
||||
strip build/setcap-static
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /build/build/setcap-static /setcap-static
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Volodymyr Kolesnykov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,67 @@
|
|||
# setcap-static
|
||||
|
||||
[![Build](https://github.com/sjinks/setcap-static/actions/workflows/build.yml/badge.svg)](https://github.com/sjinks/setcap-static/actions/workflows/build.yml)
|
||||
[![Docker CI/CD](https://github.com/sjinks/setcap-static/actions/workflows/docker.yml/badge.svg)](https://github.com/sjinks/setcap-static/actions/workflows/docker.yml)
|
||||
[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/sjinks/setcap-static.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/sjinks/setcap-static/context:cpp)
|
||||
![Docker Image Size](https://img.shields.io/docker/image-size/wildwildangel/setcap-static/latest)
|
||||
|
||||
`setcap-static` is a statically linked trimmed down version of [setcap(8)](https://linux.die.net/man/8/setcap). It sets the capabilities of the given filename to the capabilities specified.
|
||||
|
||||
## Why
|
||||
|
||||
KubeSec security guidelines suggest that the running image should be "run as a [non-root user to ensure the least privilege](https://kubesec.io/basics/containers-securitycontext-runasnonroot-true/)." However, if the containerized application needs some `root` privileges (like binding to a port less than 1024) and runs in a `scratch` image, this will not be straightforward.
|
||||
|
||||
The issue is that Docker's `COPY` command does not preserve the extended attributes; therefore, you cannot do something like this:
|
||||
|
||||
```Dockerfile
|
||||
FROM alpine:3.13 as build
|
||||
|
||||
# ...
|
||||
|
||||
RUN \
|
||||
apk add --no-cache libcap \
|
||||
&& setcap 'cap_net_bind_service=+ep' my-cool-application \
|
||||
&& apk del --no-cache libcap
|
||||
|
||||
# ...
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /path/to/my-cool-application /my-cool-application
|
||||
```
|
||||
|
||||
In the target image, `my-cool-application` will not have the capabilities set in the `build` image. Therefore, if you need to grant some capabilities to your application, you have to do it in the target image. You cannot just copy `setcap` from Alpine — because it is a dynamically linked executable (it depends on ld-musl, libcap, libc.musl).
|
||||
|
||||
Here comes `libcap-static`. It is a lightweight version of `libcap`: it can only set the capabilities on a file, it does not support all other options of `libcap`.
|
||||
|
||||
Unlike `libcap`, `libcap-static` has an option to delete itself: this can be handy for `scratch` images if you don't want to leave any other executables than your application visible to the user (or an attacker). If `libcap-static` detects that the first two characters of `argv[0]` are `/!`, it will delete itself after the successful operation.
|
||||
|
||||
For example,
|
||||
|
||||
```Dockerfile
|
||||
FROM scratch
|
||||
COPY --from=wildwildangel/setcap-static /setcap-static /!setcap-static
|
||||
COPY --from=build /build/build/tiny-ssh-honeypot /tiny-ssh-honeypot
|
||||
RUN ["/!setcap-static", "cap_net_bind_service=+ep", "/tiny-ssh-honeypot"]
|
||||
```
|
||||
|
||||
After granting the `CAP_NET_BIND_SERVICE` capability to `tiny-ssh-honeypot`, `libcap-static` will delete itself.
|
||||
|
||||
## Build
|
||||
|
||||
Build dependencies:
|
||||
* Alpine: cmake, make, libcap-dev, libcap-static
|
||||
* Ubuntu: cmake, make, libcap-dev
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=MinSizeRel
|
||||
cmake --build build --config MinSizeRel
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
setcap-static capabilities filename
|
||||
```
|
||||
|
||||
* `capabilities` is the list of capabilities in the form supported by [`cap_from_text(3)`](https://linux.die.net/man/3/cap_from_text) (or by `setcap`)
|
||||
* `filename` is the name of the file to operate on; it must not refer to a symlink.
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef A654D99F_22CB_438F_A0D3_6659AC64CE80
|
||||
#define A654D99F_22CB_438F_A0D3_6659AC64CE80
|
||||
|
||||
#cmakedefine HAVE_CAP_SET_NSOWNER 1
|
||||
|
||||
#endif /* A654D99F_22CB_438F_A0D3_6659AC64CE80 */
|
|
@ -0,0 +1,66 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/capability.h>
|
||||
#include <unistd.h>
|
||||
#include "config.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: setcap capabilities filename\n");
|
||||
return argc == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cap_t my_caps = cap_get_proc();
|
||||
if (my_caps == NULL) {
|
||||
perror("cap_get_proc");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cap_t target_caps = cap_from_text(argv[1]);
|
||||
if (target_caps == NULL) {
|
||||
perror("cap_from_text");
|
||||
cap_free(my_caps);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CAP_SETNSOWNER
|
||||
if (cap_set_nsowner(target_caps, 0)) {
|
||||
perror("cap_set_nsowner");
|
||||
cap_free(my_caps);
|
||||
cap_free(target_caps);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
cap_value_t flag = CAP_SETFCAP;
|
||||
if (cap_set_flag(my_caps, CAP_EFFECTIVE, 1, &flag, CAP_SET) != 0) {
|
||||
perror("cap_set_flag(CAP_SETFCAP)");
|
||||
cap_free(my_caps);
|
||||
cap_free(target_caps);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (cap_set_proc(my_caps) != 0) {
|
||||
perror("cap_set_proc");
|
||||
cap_free(my_caps);
|
||||
cap_free(target_caps);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cap_free(my_caps);
|
||||
|
||||
if (cap_set_file(argv[2], target_caps) != 0) {
|
||||
perror("cap_set_file");
|
||||
cap_free(target_caps);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cap_free(target_caps);
|
||||
|
||||
if (argv[0][0] == '/' && argv[0][1] == '!') {
|
||||
unlink(argv[0]);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue