2016-04-15 06:42:40 +00:00
package main
import (
"flag"
"fmt"
"os"
2016-04-15 08:54:02 +00:00
"os/exec"
2018-03-20 01:44:18 +00:00
"path/filepath"
2016-04-15 06:42:40 +00:00
"runtime"
2016-04-15 08:54:02 +00:00
"strings"
2016-04-15 06:42:40 +00:00
2016-04-15 08:54:02 +00:00
aaprofile "github.com/docker/docker/profiles/apparmor"
2018-03-20 01:52:49 +00:00
"github.com/genuinetools/binctr/version"
2016-04-15 06:42:40 +00:00
"github.com/opencontainers/runc/libcontainer"
2016-04-15 08:54:02 +00:00
"github.com/opencontainers/runc/libcontainer/apparmor"
2016-04-17 01:23:42 +00:00
_ "github.com/opencontainers/runc/libcontainer/nsenter"
2018-03-20 01:44:18 +00:00
"github.com/opencontainers/runc/libcontainer/specconv"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
2016-04-15 06:42:40 +00:00
)
const (
// BANNER is what is printed for help/info output.
BANNER = ` _ _ _
| | __ ( _ ) _ __ ___ | | _ _ __
| ' _ \ | | ' _ \ / __ | __ | ' __ |
| | _ ) | | | | | ( __ | | _ | |
| _ . __ / | _ | _ | | _ | \ ___ | \ __ | _ |
2016-04-15 08:54:02 +00:00
Fully static , self - contained container including the rootfs
that can be run by an unprivileged user .
Embedded Image : % s - % s
2016-04-15 06:42:40 +00:00
Version : % s
2018-03-20 01:52:49 +00:00
Build : % s
2016-04-15 08:54:02 +00:00
2016-04-15 06:42:40 +00:00
`
2016-04-19 05:10:18 +00:00
defaultRoot = "/tmp/binctr"
2016-04-15 08:54:02 +00:00
defaultRootfsDir = "rootfs"
defaultApparmorProfile = "docker-default"
2016-04-15 06:42:40 +00:00
)
var (
containerID string
2016-04-15 08:54:02 +00:00
pidFile string
2016-04-15 06:42:40 +00:00
root string
2018-03-20 01:44:18 +00:00
allocateTty bool
consoleSocket string
detach bool
readonly bool
2016-04-15 08:54:02 +00:00
hooks specs . Hooks
hookflags stringSlice
2018-03-20 01:52:49 +00:00
debug bool
vrsn bool
2016-04-15 06:42:40 +00:00
2016-04-15 08:54:02 +00:00
// IMAGE is the name of the image that is embedded at compile time.
IMAGE = "alpine"
// IMAGESHA is the sha digest of the image that is embedded at compile time.
IMAGESHA = "sha256:70c557e50ed630deed07cbb0dc4d28aa0f2a485cf7af124cc48f06bce83f784b"
2016-04-15 06:42:40 +00:00
)
2016-04-15 08:54:02 +00:00
// stringSlice is a slice of strings
type stringSlice [ ] string
// implement the flag interface for stringSlice
func ( s * stringSlice ) String ( ) string {
return fmt . Sprintf ( "%s" , * s )
}
func ( s * stringSlice ) Set ( value string ) error {
* s = append ( * s , value )
return nil
}
func ( s stringSlice ) ParseHooks ( ) ( hooks specs . Hooks , err error ) {
for _ , v := range s {
parts := strings . SplitN ( v , ":" , 2 )
if len ( parts ) <= 1 {
return hooks , fmt . Errorf ( "parsing %s as hook_name:exec failed" , v )
}
cmd := strings . Split ( parts [ 1 ] , " " )
exec , err := exec . LookPath ( cmd [ 0 ] )
if err != nil {
return hooks , fmt . Errorf ( "looking up exec path for %s failed: %v" , cmd [ 0 ] , err )
}
hook := specs . Hook {
Path : exec ,
}
if len ( cmd ) > 1 {
hook . Args = cmd [ : 1 ]
}
switch parts [ 0 ] {
case "prestart" :
hooks . Prestart = append ( hooks . Prestart , hook )
case "poststart" :
hooks . Poststart = append ( hooks . Poststart , hook )
case "poststop" :
hooks . Poststop = append ( hooks . Poststop , hook )
default :
return hooks , fmt . Errorf ( "%s is not a valid hook, try 'prestart', 'poststart', or 'poststop'" , parts [ 0 ] )
}
}
return hooks , nil
}
2016-04-15 06:42:40 +00:00
func init ( ) {
// Parse flags
2016-04-15 08:54:02 +00:00
flag . StringVar ( & containerID , "id" , IMAGE , "container ID" )
flag . StringVar ( & pidFile , "pid-file" , "" , "specify the file to write the process id to" )
2016-04-15 06:42:40 +00:00
flag . StringVar ( & root , "root" , defaultRoot , "root directory of container state, should be tmpfs" )
2016-04-15 08:54:02 +00:00
flag . Var ( & hookflags , "hook" , "Hooks to prefill into spec file. (ex. --hook prestart:netns)" )
flag . BoolVar ( & allocateTty , "t" , true , "allocate a tty for the container" )
2018-03-20 01:44:18 +00:00
flag . StringVar ( & consoleSocket , "console-socket" , "" , "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal" )
2016-04-15 08:54:02 +00:00
flag . BoolVar ( & detach , "d" , false , "detach from the container's process" )
flag . BoolVar ( & readonly , "read-only" , false , "make container filesystem readonly" )
2018-03-20 01:52:49 +00:00
flag . BoolVar ( & vrsn , "version" , false , "print version and exit" )
flag . BoolVar ( & vrsn , "v" , false , "print version and exit (shorthand)" )
2016-04-15 08:54:02 +00:00
flag . BoolVar ( & debug , "D" , false , "run in debug mode" )
2016-04-15 06:42:40 +00:00
flag . Usage = func ( ) {
2018-03-20 01:52:49 +00:00
fmt . Fprint ( os . Stderr , fmt . Sprintf ( BANNER , IMAGE , IMAGESHA , version . VERSION , version . GITCOMMIT ) )
2016-04-15 06:42:40 +00:00
flag . PrintDefaults ( )
}
flag . Parse ( )
2018-03-20 01:52:49 +00:00
if vrsn {
fmt . Printf ( "%s, commit: %s, image: %s, image digest: %s" , version . VERSION , version . GITCOMMIT , IMAGE , IMAGESHA )
2016-04-15 06:42:40 +00:00
os . Exit ( 0 )
}
2018-03-20 01:44:18 +00:00
// Set log level.
2016-04-15 06:42:40 +00:00
if debug {
logrus . SetLevel ( logrus . DebugLevel )
}
2016-04-15 08:54:02 +00:00
2018-03-20 01:44:18 +00:00
// Parse the hook flags.
2016-04-15 08:54:02 +00:00
var err error
hooks , err = hookflags . ParseHooks ( )
if err != nil {
logrus . Fatal ( err )
}
2018-03-20 01:44:18 +00:00
// Convert pid-file to an absolute path so we can write to the
// right file after chdir to bundle.
if pidFile != "" {
pidFile , err = filepath . Abs ( pidFile )
if err != nil {
logrus . Fatal ( err )
}
}
// Get the absolute path to the root.
root , err = filepath . Abs ( root )
if err != nil {
logrus . Fatal ( err )
}
2016-04-15 06:42:40 +00:00
}
2016-04-15 08:54:02 +00:00
//go:generate go run generate.go
2016-04-15 06:42:40 +00:00
func main ( ) {
if len ( os . Args ) > 1 && os . Args [ 1 ] == "init" {
runInit ( )
return
}
2018-03-20 01:44:18 +00:00
// Initialize the spec.
spec := specconv . Example ( )
2016-04-15 08:54:02 +00:00
2018-03-20 01:44:18 +00:00
// Set the spec to be rootless.
specconv . ToRootless ( spec )
2016-04-15 08:54:02 +00:00
2018-03-20 01:44:18 +00:00
// Setup readonly fs in spec.
2016-04-15 08:54:02 +00:00
spec . Root . Readonly = readonly
2018-03-20 01:44:18 +00:00
// Setup tty in spec.
2016-04-15 08:54:02 +00:00
spec . Process . Terminal = allocateTty
2018-03-20 01:44:18 +00:00
// Pass in any hooks to the spec.
spec . Hooks = & hooks
// Set the default seccomp profile.
spec . Linux . Seccomp = defaultSeccompProfile
2016-04-15 08:54:02 +00:00
2018-03-20 01:44:18 +00:00
// Install the default apparmor profile.
2016-04-15 08:54:02 +00:00
if apparmor . IsEnabled ( ) {
2018-03-20 01:44:18 +00:00
// Check if we have the docker-default apparmor profile loaded.
2017-06-14 11:10:02 +00:00
if _ , err := aaprofile . IsLoaded ( defaultApparmorProfile ) ; err != nil {
2016-04-16 23:05:09 +00:00
logrus . Warnf ( "AppArmor enabled on system but the %s profile is not loaded. apparmor_parser needs root to load a profile so we can't do it for you." , defaultApparmorProfile )
2016-04-15 08:54:02 +00:00
} else {
spec . Process . ApparmorProfile = defaultApparmorProfile
}
}
2018-03-20 01:44:18 +00:00
// Unpack the rootfs.
2016-04-15 08:54:02 +00:00
if err := unpackRootfs ( spec ) ; err != nil {
2016-04-15 06:42:40 +00:00
logrus . Fatal ( err )
}
2018-03-20 01:44:18 +00:00
// Start the container.
status , err := startContainer ( spec , containerID , pidFile , consoleSocket , root , detach )
2016-04-15 06:42:40 +00:00
if err != nil {
logrus . Fatal ( err )
}
2018-03-20 01:44:18 +00:00
// Remove the rootfs after the container has exited.
2016-04-15 08:54:02 +00:00
if err := os . RemoveAll ( defaultRootfsDir ) ; err != nil {
logrus . Warnf ( "removing rootfs failed: %v" , err )
}
2018-03-20 01:44:18 +00:00
// Exit with the container's exit status.
2016-04-15 06:42:40 +00:00
os . Exit ( status )
}
func runInit ( ) {
2016-04-15 08:54:02 +00:00
runtime . GOMAXPROCS ( 1 )
runtime . LockOSThread ( )
factory , _ := libcontainer . New ( "" )
if err := factory . StartInitialization ( ) ; err != nil {
// as the error is sent back to the parent there is no need to log
// or write it to stderr because the parent process will handle this
os . Exit ( 1 )
2016-04-15 06:42:40 +00:00
}
2016-04-15 08:54:02 +00:00
panic ( "libcontainer: container init failed to exec" )
2016-04-15 06:42:40 +00:00
}