unicorn-lockdown 0.13.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG +14 -0
 - data/MIT-LICENSE +1 -1
 - data/README.rdoc +147 -123
 - data/bin/unicorn-lockdown-add +56 -58
 - data/bin/unicorn-lockdown-setup +4 -4
 - data/lib/roda/plugins/pg_disconnect.rb +5 -6
 - data/lib/unicorn-lockdown.rb +65 -120
 - data/lib/unveiler.rb +38 -0
 - metadata +5 -6
 - data/lib/chrooter.rb +0 -153
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 931209c4514dbb9f15da327fac6ed81f82fed2a504c500a34aa5f6e2624cde5a
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 160c9cf285e6c4f8abeb38bce3d1b7006f236607e6f18e606fdcbd8b53f73baa
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 9f457aefb1571441815165ef9fb032f2de71547debfa81d9a7d4a441019e0671e00258b77bdb4fdca29dca677a5b5e795c65a7f53483cbf67bd12ef2950e4545
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: de3ee5209b1cb08dd0918448689868cc7b5c423ec07c929df7a4475d222436b32ea271f972f789a7ee13ffcffc8a7bbafd78950741c40392d49b8c69af69473e
         
     | 
    
        data/CHANGELOG
    CHANGED
    
    | 
         @@ -1,3 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            = 1.0.0 (2020-11-09)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            * Require unicorn-lockdown-add -o and -u options, and require options have arguments (jeremyevans)
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            * Switch to starting unicorn master process as application user, drop chroot support, require unveil (jeremyevans)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            * Remove chrooter library (jeremyevans)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            * Add unveiler library for testing pledged/unveiled applications, similar to chrooter but smaller (jeremyevans)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            * Add :master_execpledge option to Unicorn.lockdown, for initial pledge of worker processes (jeremyevans)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            * Add :master_pledge option to Unicorn.lockdown, for pledging the master process (jeremyevans)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       1 
15 
     | 
    
         
             
            = 0.13.0 (2019-07-09)
         
     | 
| 
       2 
16 
     | 
    
         | 
| 
       3 
17 
     | 
    
         
             
            * Add Chrooter.unveil for using unveil in tests (jeremyevans)
         
     | 
    
        data/MIT-LICENSE
    CHANGED
    
    
    
        data/README.rdoc
    CHANGED
    
    | 
         @@ -1,32 +1,24 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            = unicorn-lockdown
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            unicorn-lockdown is a helper library for running Unicorn on OpenBSD  
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
            file system permissions), drops root privileges and then runs as the application
         
     | 
| 
       13 
     | 
    
         
            -
            user (privdrop), then runs pledge to limit the allowed system calls to the minimum
         
     | 
| 
       14 
     | 
    
         
            -
            required to run the application.
         
     | 
| 
      
 3 
     | 
    
         
            +
            unicorn-lockdown is a helper library for running Unicorn on OpenBSD with pledge,
         
     | 
| 
      
 4 
     | 
    
         
            +
            unveil, and fork+exec for increased security.
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            With unicorn-lockdown, unicorn should be started as the application user, which
         
     | 
| 
      
 7 
     | 
    
         
            +
            should be different than the user that owns the application's files.  unicorn
         
     | 
| 
      
 8 
     | 
    
         
            +
            will pledge the master process, then fork worker processes.  The worker
         
     | 
| 
      
 9 
     | 
    
         
            +
            processes will re-exec (fork+exec), then load the application, then set unveil
         
     | 
| 
      
 10 
     | 
    
         
            +
            to restrict file system access, then pledge to limit the allowed system calls
         
     | 
| 
      
 11 
     | 
    
         
            +
            at runtime.
         
     | 
| 
       15 
12 
     | 
    
         | 
| 
       16 
13 
     | 
    
         
             
            == Assumptions
         
     | 
| 
       17 
14 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
            unicorn-lockdown assumes you are using OpenBSD 6. 
     | 
| 
       19 
     | 
    
         
            -
            rubyXY-unicorn packages installed, and that 
     | 
| 
       20 
     | 
    
         
            -
            the PATH to the appropriate unicornXY executable.
         
     | 
| 
      
 15 
     | 
    
         
            +
            unicorn-lockdown assumes you are using OpenBSD 6.6+ with the nginx and
         
     | 
| 
      
 16 
     | 
    
         
            +
            <tt>rubyXY-unicorn</tt> and <tt>rubyXY-pledge</tt> packages installed, and that
         
     | 
| 
      
 17 
     | 
    
         
            +
            you have a +unicorn+ symlink in the PATH to the appropriate +unicornXY+ executable.
         
     | 
| 
       21 
18 
     | 
    
         | 
| 
       22 
19 
     | 
    
         
             
            It also assumes you have a SMTP server listening on localhost port 25 to
         
     | 
| 
       23 
20 
     | 
    
         
             
            receive notification emails of worker crashes, if you are notifying for those.
         
     | 
| 
       24 
21 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
            If using unveil instead of chroot to limit file system access, you must be
         
     | 
| 
       26 
     | 
    
         
            -
            using OpenBSD 6.6+ (or 6.5-current after July 2019), one of the OpenBSD Ruby
         
     | 
| 
       27 
     | 
    
         
            -
            ports with backported support for unveil to work correctly with +File.realpath+,
         
     | 
| 
       28 
     | 
    
         
            -
            and version 1.2.0+ of the pledge gem.
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
22 
     | 
    
         
             
            == Usage
         
     | 
| 
       31 
23 
     | 
    
         | 
| 
       32 
24 
     | 
    
         
             
            === unicorn-lockdown-setup
         
     | 
| 
         @@ -39,14 +31,14 @@ the following as root after reading the file and understanding what it does. 
     | 
|
| 
       39 
31 
     | 
    
         
             
            Briefly, the configuration this uses the following directories:
         
     | 
| 
       40 
32 
     | 
    
         | 
| 
       41 
33 
     | 
    
         
             
            /var/www/sockets :: Stores unix sockets that Unicorn listens on and Nginx uses.
         
     | 
| 
       42 
     | 
    
         
            -
            /var/www/ 
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
      
 34 
     | 
    
         
            +
            /var/www/request-error-info :: Stores temporary files for each request with request info,
         
     | 
| 
      
 35 
     | 
    
         
            +
                                           used for crash notifications
         
     | 
| 
       44 
36 
     | 
    
         
             
            /var/log/unicorn :: Stores unicorn log files, one per application
         
     | 
| 
       45 
37 
     | 
    
         
             
            /var/log/nginx :: Stores nginx log files, two per application, one for access
         
     | 
| 
       46 
38 
     | 
    
         
             
                              and one for errors
         
     | 
| 
       47 
39 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
            This adds a _unicorn group that all  
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
            This adds a _unicorn group that all application users will use as one of their
         
     | 
| 
      
 41 
     | 
    
         
            +
            groups, as well as a /etc/rc.d/rc.unicorn file that the application
         
     | 
| 
       50 
42 
     | 
    
         
             
            /etc/rc.d/unicorn_* files will use.
         
     | 
| 
       51 
43 
     | 
    
         | 
| 
       52 
44 
     | 
    
         
             
            === unicorn-lockdown-add
         
     | 
| 
         @@ -54,30 +46,38 @@ group, as well as a /etc/rc.d/rc.unicorn file that the per application 
     | 
|
| 
       54 
46 
     | 
    
         
             
            For each application you want to run with unicorn lockdown, run the following
         
     | 
| 
       55 
47 
     | 
    
         
             
            as root, again after reading the file and understanding what it does:
         
     | 
| 
       56 
48 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
              unicorn-lockdown-add
         
     | 
| 
      
 49 
     | 
    
         
            +
              unicorn-lockdown-add -o $owner -u $user $app_name
         
     | 
| 
       58 
50 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
            Here's  
     | 
| 
      
 51 
     | 
    
         
            +
            Here's the usage:
         
     | 
| 
       60 
52 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
              Usage: unicorn-lockdown-add [options] app_name
         
     | 
| 
      
 53 
     | 
    
         
            +
              Usage: unicorn-lockdown-add -o owner -u user [options] app_name
         
     | 
| 
       62 
54 
     | 
    
         
             
              Options:
         
     | 
| 
       63 
     | 
    
         
            -
                  -c 
     | 
| 
       64 
     | 
    
         
            -
                  -d 
     | 
| 
       65 
     | 
    
         
            -
                  -f 
     | 
| 
       66 
     | 
    
         
            -
                  -o 
     | 
| 
       67 
     | 
    
         
            -
                  -u 
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
      
 55 
     | 
    
         
            +
                  -c RACKUP_FILE                   rackup configuration file
         
     | 
| 
      
 56 
     | 
    
         
            +
                  -d DIR                           application directory name
         
     | 
| 
      
 57 
     | 
    
         
            +
                  -f UNICORN_FILE                  unicorn configuration file relative to application directory
         
     | 
| 
      
 58 
     | 
    
         
            +
                  -o OWNER                         operating system application owner
         
     | 
| 
      
 59 
     | 
    
         
            +
                  -u USER                          operating system user to run application
         
     | 
| 
      
 60 
     | 
    
         
            +
                      --uid UID                    user id to use if creating the user when -U is specified
         
     | 
| 
      
 61 
     | 
    
         
            +
                  -h, -?, --help                   Show this message
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            The <tt>-o</tt> and <tt>-u</tt> options are required.  Default values for other options are:
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            <tt>-c</tt> :: None, Unicorn will use <tt>config.ru</tt> by default.
         
     | 
| 
      
 66 
     | 
    
         
            +
            <tt>-d</tt> :: Same as +app_name+.  The value provided should a relative path under <tt>/var/www</tt>.
         
     | 
| 
      
 67 
     | 
    
         
            +
            <tt>-f</tt> :: <tt>unicorn.conf</tt>.  This file should be relative to +dir+.
         
     | 
| 
      
 68 
     | 
    
         
            +
            <tt>--uid</tt> :: The uid automatically generated by +useradd+.
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
            The owner <tt>-o</tt> and the user <tt>-u</tt> should be different.  The user is the user the
         
     | 
| 
      
 71 
     | 
    
         
            +
            application runs as, and should have read-only access to the application directory,
         
     | 
| 
      
 72 
     | 
    
         
            +
            other than locations where you want the application user to be able to modify files
         
     | 
| 
      
 73 
     | 
    
         
            +
            at runtime.  The owner is the user that owns the application directory and can make
         
     | 
| 
      
 74 
     | 
    
         
            +
            modifications to the application.
         
     | 
| 
       75 
75 
     | 
    
         | 
| 
       76 
76 
     | 
    
         
             
            === unicorn-lockdown
         
     | 
| 
       77 
77 
     | 
    
         | 
| 
       78 
78 
     | 
    
         
             
            unicorn-lockdown is the library required in your unicorn configuration
         
     | 
| 
       79 
79 
     | 
    
         
             
            file for the application, to handle configuring unicorn to run the app
         
     | 
| 
       80 
     | 
    
         
            -
            with  
     | 
| 
      
 80 
     | 
    
         
            +
            with fork+exec, unveil, and pledge.
         
     | 
| 
       81 
81 
     | 
    
         | 
| 
       82 
82 
     | 
    
         
             
            When you run unicorn-lockdown-add, it will create the unicorn configuration
         
     | 
| 
       83 
83 
     | 
    
         
             
            file for the app if one does not already exist, looking similar to:
         
     | 
| 
         @@ -86,48 +86,75 @@ file for the app if one does not already exist, looking similar to: 
     | 
|
| 
       86 
86 
     | 
    
         | 
| 
       87 
87 
     | 
    
         
             
              Unicorn.lockdown(self,
         
     | 
| 
       88 
88 
     | 
    
         
             
                :app=>"app_name",
         
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
                 
     | 
| 
       91 
     | 
    
         
            -
                :email=>'root' 
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                # Update this with correct email
         
     | 
| 
      
 91 
     | 
    
         
            +
                :email=>'root',
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                # More pledges may be needed depending on application
         
     | 
| 
      
 94 
     | 
    
         
            +
                :pledge=>'rpath prot_exec inet unix',
         
     | 
| 
      
 95 
     | 
    
         
            +
                :master_pledge=>'rpath prot_exec cpath wpath inet proc exec',
         
     | 
| 
      
 96 
     | 
    
         
            +
                :master_execpledge=>'stdio rpath prot_exec inet unix cpath wpath unveil',
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                # More unveils may be needed depending on application
         
     | 
| 
      
 99 
     | 
    
         
            +
                :unveil=>{
         
     | 
| 
      
 100 
     | 
    
         
            +
                  'views'=>'r'
         
     | 
| 
      
 101 
     | 
    
         
            +
                },
         
     | 
| 
      
 102 
     | 
    
         
            +
                :dev_unveil=>{
         
     | 
| 
      
 103 
     | 
    
         
            +
                  'models'=>'r'
         
     | 
| 
      
 104 
     | 
    
         
            +
                },
         
     | 
| 
       92 
105 
     | 
    
         
             
              )
         
     | 
| 
       93 
106 
     | 
    
         | 
| 
       94 
107 
     | 
    
         
             
            Unicorn.lockdown options:
         
     | 
| 
       95 
108 
     | 
    
         | 
| 
       96 
109 
     | 
    
         
             
            :app :: (required) a short string for the name of the application, used
         
     | 
| 
       97 
110 
     | 
    
         
             
                    for socket/log file names and in notifications
         
     | 
| 
       98 
     | 
    
         
            -
            : 
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
                       
     | 
| 
       101 
     | 
    
         
            -
                      process primary group, the second as the owner of the unicorn
         
     | 
| 
       102 
     | 
    
         
            -
                      log files.
         
     | 
| 
      
 111 
     | 
    
         
            +
            :email :: (optional) an email address to use for notifications when the
         
     | 
| 
      
 112 
     | 
    
         
            +
                      worker process crashes or an unhandled exception is raised by
         
     | 
| 
      
 113 
     | 
    
         
            +
                      the application or middleware.
         
     | 
| 
       103 
114 
     | 
    
         
             
            :pledge :: (optional) a pledge string to limit the allowed system calls
         
     | 
| 
       104 
115 
     | 
    
         
             
                       after privileges have been dropped
         
     | 
| 
       105 
     | 
    
         
            -
            : 
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
            : 
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
      
 116 
     | 
    
         
            +
            :master_pledge :: (optional) The string to use when pledging the master process before
         
     | 
| 
      
 117 
     | 
    
         
            +
                              spawning worker processes
         
     | 
| 
      
 118 
     | 
    
         
            +
            :master_execpledge :: (optional) The pledge string for processes spawned by the master
         
     | 
| 
      
 119 
     | 
    
         
            +
                                  process (i.e. worker processes before loading the app)
         
     | 
| 
      
 120 
     | 
    
         
            +
            :unveil :: (required) a hash of paths to limit file system access, passed
         
     | 
| 
      
 121 
     | 
    
         
            +
                       to +Pledge.unveil+.
         
     | 
| 
      
 122 
     | 
    
         
            +
            :dev_unveil :: (optional) a hash of paths to limit file system, merged into the :unveil
         
     | 
| 
      
 123 
     | 
    
         
            +
                           option paths if in the development environment.  Useful if you are
         
     | 
| 
       110 
124 
     | 
    
         
             
                           allowing more access in development, such as access needed
         
     | 
| 
       111 
125 
     | 
    
         
             
                           for file reloading.
         
     | 
| 
       112 
     | 
    
         
            -
            :email :: (optional) an email address to use for notifications when the
         
     | 
| 
       113 
     | 
    
         
            -
                      worker process crashes or an unhandled exception is raised by
         
     | 
| 
       114 
     | 
    
         
            -
                      the application or middleware.
         
     | 
| 
       115 
126 
     | 
    
         | 
| 
       116 
127 
     | 
    
         
             
            With this example pledge:
         
     | 
| 
       117 
128 
     | 
    
         | 
| 
       118 
     | 
    
         
            -
            * rpath is needed to read files 
     | 
| 
      
 129 
     | 
    
         
            +
            * rpath is needed to read files
         
     | 
| 
       119 
130 
     | 
    
         
             
            * prot_exec is needed in most cases
         
     | 
| 
       120 
131 
     | 
    
         
             
            * unix is needed for the unix socket to nginx
         
     | 
| 
       121 
     | 
    
         
            -
            * inet is not needed in all cases, but  
     | 
| 
       122 
     | 
    
         
            -
              of network access 
     | 
| 
       123 
     | 
    
         
            -
               
     | 
| 
       124 
     | 
    
         
            -
               
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
      
 132 
     | 
    
         
            +
            * inet is not needed in all cases, but most applications need some form
         
     | 
| 
      
 133 
     | 
    
         
            +
              of network access, and it is needed by default for emailing about
         
     | 
| 
      
 134 
     | 
    
         
            +
              exceptions that occur without process crashes.  pf (OpenBSD's firewall)
         
     | 
| 
      
 135 
     | 
    
         
            +
              should be used to limit access for the application's operating system
         
     | 
| 
      
 136 
     | 
    
         
            +
              user to the minimum necessary access needed.
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
            With this example master pledge:
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
            * rpath is needed to read files
         
     | 
| 
      
 141 
     | 
    
         
            +
            * prot_exec is needed in most cases
         
     | 
| 
      
 142 
     | 
    
         
            +
            * cpath and wpath are needed to unlink the request error files
         
     | 
| 
      
 143 
     | 
    
         
            +
            * inet is needed to send emails for worker crashes
         
     | 
| 
      
 144 
     | 
    
         
            +
            * proc and exec are needed to spawn worker processes
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
            With this examle master exec pledge:
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
            * stdio must be added because ruby-pledge doesn't add it automatically
         
     | 
| 
      
 149 
     | 
    
         
            +
              to execpromises, and Ruby requires it
         
     | 
| 
      
 150 
     | 
    
         
            +
            * rpath, prot_exec, unix, inet are needed for the worker (see above)
         
     | 
| 
      
 151 
     | 
    
         
            +
            * cpath and wpath are needed to create the request error files
         
     | 
| 
      
 152 
     | 
    
         
            +
            * unveil is needed to restrict file system access
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
            unicorn-lockdown has specific support for allowing emails to be sent
         
     | 
| 
      
 155 
     | 
    
         
            +
            for Unicorn worker crashes (e.g. pledge violations) and unhandled
         
     | 
| 
      
 156 
     | 
    
         
            +
            application exceptions (e.g. pledge violations).  Additionally,
         
     | 
| 
      
 157 
     | 
    
         
            +
            unicorn-lockdown modifies unicorn's process status line in a
         
     | 
| 
       131 
158 
     | 
    
         
             
            way that allows it to be controllable via OpenBSD's rcctl program for
         
     | 
| 
       132 
159 
     | 
    
         
             
            stopping/starting/reloading/restarting daemons.
         
     | 
| 
       133 
160 
     | 
    
         | 
| 
         @@ -136,7 +163,12 @@ with the expectation of an Nginx limit of 10MB, such that all client 
     | 
|
| 
       136 
163 
     | 
    
         
             
            requests will be buffered in memory and unicorn will not need to write
         
     | 
| 
       137 
164 
     | 
    
         
             
            temporary files to disk.  If this limit is not correct for your
         
     | 
| 
       138 
165 
     | 
    
         
             
            application, please call client_body_buffer_size after calling
         
     | 
| 
       139 
     | 
    
         
            -
            Unicorn.lockdown to set an appropriate limit.
         
     | 
| 
      
 166 
     | 
    
         
            +
            Unicorn.lockdown to set an appropriate limit.  Note that rack still
         
     | 
| 
      
 167 
     | 
    
         
            +
            creates temporary files for file uploads by default, you'll need to
         
     | 
| 
      
 168 
     | 
    
         
            +
            configure rack to disallow file uploads if your application does not
         
     | 
| 
      
 169 
     | 
    
         
            +
            need to accept uploaded files and you don't want file upload attempts
         
     | 
| 
      
 170 
     | 
    
         
            +
            to cause pledge violations.  With Roda, you can use the
         
     | 
| 
      
 171 
     | 
    
         
            +
            disallow_file_uploads plugin to prevent file upload attempts.
         
     | 
| 
       140 
172 
     | 
    
         | 
| 
       141 
173 
     | 
    
         
             
            When Unicorn.lockdown is used with the :email option, if the worker
         
     | 
| 
       142 
174 
     | 
    
         
             
            process crashes, it will email the address using the contents specified
         
     | 
| 
         @@ -155,6 +187,8 @@ and then at the top of the route block, do: 
     | 
|
| 
       155 
187 
     | 
    
         | 
| 
       156 
188 
     | 
    
         
             
              if defined?(Unicorn) && Unicorn.respond_to?(:write_request)
         
     | 
| 
       157 
189 
     | 
    
         
             
                Unicorn.write_request(error_email_content("Unicorn Worker Process Crash"))
         
     | 
| 
      
 190 
     | 
    
         
            +
                # or
         
     | 
| 
      
 191 
     | 
    
         
            +
                Unicorn.write_request(error_mail_content("Unicorn Worker Process Crash"))
         
     | 
| 
       158 
192 
     | 
    
         
             
              end
         
     | 
| 
       159 
193 
     | 
    
         | 
| 
       160 
194 
     | 
    
         
             
            If you don't have useful information in the request file, an email will
         
     | 
| 
         @@ -166,10 +200,20 @@ underlying problem. 
     | 
|
| 
       166 
200 
     | 
    
         | 
| 
       167 
201 
     | 
    
         
             
            If you are using PostgreSQL as the database for the application, and using
         
     | 
| 
       168 
202 
     | 
    
         
             
            unix sockets to connect the application to the database, if the database
         
     | 
| 
       169 
     | 
    
         
            -
            is restarted, the application will no longer be able to connect to it 
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
      
 203 
     | 
    
         
            +
            is restarted, the application will no longer be able to connect to it unless
         
     | 
| 
      
 204 
     | 
    
         
            +
            you unveil the path the database socket (stored in /tmp by default).  It can
         
     | 
| 
      
 205 
     | 
    
         
            +
            be a better approach security wise not to allow this, to prevent the
         
     | 
| 
      
 206 
     | 
    
         
            +
            application from being able to establish new database connections with
         
     | 
| 
      
 207 
     | 
    
         
            +
            potentially different credentials, as a mitigation in case the server is
         
     | 
| 
      
 208 
     | 
    
         
            +
            compromised.
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
            To allow the application to handle cases where the database is disconnected,
         
     | 
| 
      
 211 
     | 
    
         
            +
            such as due to a restart of PostgreSQL, you can kill the worker process if
         
     | 
| 
      
 212 
     | 
    
         
            +
            a disconnect error is detected, and have the master process then spawn a new
         
     | 
| 
      
 213 
     | 
    
         
            +
            worker.
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
            The roda-pg_disconnect plugin is a plugin for the roda web toolkit to kill the
         
     | 
| 
      
 216 
     | 
    
         
            +
            worker process after handling the connection if it detects the database
         
     | 
| 
       173 
217 
     | 
    
         
             
            connection has been lost.  This plugin assumes the use of the Sequel database
         
     | 
| 
       174 
218 
     | 
    
         
             
            library and postgres adapter with the pg driver.
         
     | 
| 
       175 
219 
     | 
    
         | 
| 
         @@ -178,78 +222,58 @@ In your Roda application: 
     | 
|
| 
       178 
222 
     | 
    
         
             
              # Sometime before loading the error_handler plugin
         
     | 
| 
       179 
223 
     | 
    
         
             
              plugin :pg_disconnect
         
     | 
| 
       180 
224 
     | 
    
         | 
| 
      
 225 
     | 
    
         
            +
            To specifically restrict access to the database socket even when access to
         
     | 
| 
      
 226 
     | 
    
         
            +
            /tmp is allowed, you can unveil the database socket path with no permissions:
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
              '/tmp/.s.PGSQL.5432'=>''
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
            Note that there are potentially other security issues with unveiling access
         
     | 
| 
      
 231 
     | 
    
         
            +
            to /tmp beyond granting access to the database server, so it is recommended
         
     | 
| 
      
 232 
     | 
    
         
            +
            you do not unveil it.  If the application needs a directory for temporary
         
     | 
| 
      
 233 
     | 
    
         
            +
            files (e.g. for handling uploaded files with rack), you can set the +TMPDIR+
         
     | 
| 
      
 234 
     | 
    
         
            +
            environment variable to an appropriate directory that is writable by the
         
     | 
| 
      
 235 
     | 
    
         
            +
            application user and not other users, and most web applications will respect
         
     | 
| 
      
 236 
     | 
    
         
            +
            that (assuming they use the tmpfile/tmpdir libraries in the standard library).
         
     | 
| 
      
 237 
     | 
    
         
            +
             
     | 
| 
       181 
238 
     | 
    
         
             
            === rack-email_exceptions
         
     | 
| 
       182 
239 
     | 
    
         | 
| 
       183 
     | 
    
         
            -
            rack-email_exceptions is a rack middleware designed to  
     | 
| 
       184 
     | 
    
         
            -
            middleware  
     | 
| 
      
 240 
     | 
    
         
            +
            rack-email_exceptions is a rack middleware designed to wrap all other
         
     | 
| 
      
 241 
     | 
    
         
            +
            middleware and the application.  It rescues unhandled exceptions
         
     | 
| 
       185 
242 
     | 
    
         
             
            raised by subsequent middleware or the application itself.
         
     | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
       187 
243 
     | 
    
         
             
            Unicorn.lockdown will automatically setup this middleware if the :email
         
     | 
| 
       188 
     | 
    
         
            -
            option is used 
     | 
| 
       189 
     | 
    
         
            -
            such that it wraps the application and all other middleware.
         
     | 
| 
      
 244 
     | 
    
         
            +
            option is used.
         
     | 
| 
       190 
245 
     | 
    
         | 
| 
       191 
246 
     | 
    
         
             
            It is possible to use this middleware manually:
         
     | 
| 
       192 
247 
     | 
    
         | 
| 
       193 
248 
     | 
    
         
             
              require 'rack/email_exceptions'
         
     | 
| 
       194 
249 
     | 
    
         
             
              use Rack::EmailExceptions, "app_name", 'foo@example.com'
         
     | 
| 
       195 
250 
     | 
    
         | 
| 
       196 
     | 
    
         
            -
            ===  
     | 
| 
      
 251 
     | 
    
         
            +
            === unveiler
         
     | 
| 
       197 
252 
     | 
    
         | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
       199 
     | 
    
         
            -
             
     | 
| 
       200 
     | 
    
         
            -
             
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
            the application into production, such as a file being read from outside
         
     | 
| 
       203 
     | 
    
         
            -
            the chroot.
         
     | 
| 
      
 253 
     | 
    
         
            +
            unveiler is a library designed to help with testing applications that
         
     | 
| 
      
 254 
     | 
    
         
            +
            use pledge and unveil.  If you are running your application pledged and
         
     | 
| 
      
 255 
     | 
    
         
            +
            unveiled, you want your tests to run pledged and unveiled to find
         
     | 
| 
      
 256 
     | 
    
         
            +
            problems.
         
     | 
| 
       204 
257 
     | 
    
         | 
| 
       205 
     | 
    
         
            -
             
     | 
| 
      
 258 
     | 
    
         
            +
            unveiler assumes you are using minitest for testing.  To use unveiler:
         
     | 
| 
       206 
259 
     | 
    
         | 
| 
       207 
260 
     | 
    
         
             
              require 'minitest/autorun'
         
     | 
| 
       208 
     | 
    
         
            -
              require ' 
     | 
| 
       209 
     | 
    
         
            -
              at_exit{Chrooter.chroot('user_name', 'rpath prot_exec inet unix')}
         
     | 
| 
       210 
     | 
    
         
            -
             
     | 
| 
       211 
     | 
    
         
            -
            If you run your specs as a regular user, it will execute them without
         
     | 
| 
       212 
     | 
    
         
            -
            chrooting, but in a way that can still catch some problems that occur
         
     | 
| 
       213 
     | 
    
         
            -
            when chrooted.  If you run your specs as root, it will chroot to
         
     | 
| 
       214 
     | 
    
         
            -
            the current directory after loading the specs, then drop
         
     | 
| 
       215 
     | 
    
         
            -
            privileges to the user given (and optionally pledging using the given
         
     | 
| 
       216 
     | 
    
         
            -
            pledge string), then run the specs.
         
     | 
| 
       217 
     | 
    
         
            -
             
     | 
| 
       218 
     | 
    
         
            -
            If you want to use unveil instead of chroot, you can use
         
     | 
| 
       219 
     | 
    
         
            -
            +Chrooter.unveil+:
         
     | 
| 
       220 
     | 
    
         
            -
             
     | 
| 
       221 
     | 
    
         
            -
              require 'minitest/autorun'
         
     | 
| 
       222 
     | 
    
         
            -
              require 'chrooter'
         
     | 
| 
      
 261 
     | 
    
         
            +
              require 'unveiler'
         
     | 
| 
       223 
262 
     | 
    
         
             
              at_exit do
         
     | 
| 
       224 
     | 
    
         
            -
                 
     | 
| 
       225 
     | 
    
         
            -
                  'views' => 'r',
         
     | 
| 
       226 
     | 
    
         
            -
                  'rack' => :gem,
         
     | 
| 
       227 
     | 
    
         
            -
                  'mail' => :gem,
         
     | 
| 
       228 
     | 
    
         
            -
                )
         
     | 
| 
      
 263 
     | 
    
         
            +
                Unveiler.pledge_and_unveil('rpath prot_exec inet unix', 'views' => 'r')
         
     | 
| 
       229 
264 
     | 
    
         
             
              end
         
     | 
| 
       230 
265 
     | 
    
         | 
| 
       231 
     | 
    
         
            -
            One advantage of using unveil instead of chroot is that the unveils
         
     | 
| 
       232 
     | 
    
         
            -
            will take effect even when not starting the tests as root.
         
     | 
| 
       233 
     | 
    
         
            -
             
     | 
| 
       234 
266 
     | 
    
         
             
            == autoload
         
     | 
| 
       235 
267 
     | 
    
         | 
| 
       236 
     | 
    
         
            -
            As you'll find out if you try to run your applications with  
     | 
| 
       237 
     | 
    
         
            -
            autoload  
     | 
| 
       238 
     | 
    
         
            -
             
     | 
| 
       239 
     | 
    
         
            -
            If you use other gems that use 
     | 
| 
       240 
     | 
    
         
            -
             
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
       243 
     | 
    
         
            -
            If you have to use libraries that use autoload, it is recommended that
         
     | 
| 
       244 
     | 
    
         
            -
            you use the support for unveil instead of using chroot, and allow
         
     | 
| 
       245 
     | 
    
         
            -
            access to the given gems.  unicorn-lockdown will automatically unveil
         
     | 
| 
       246 
     | 
    
         
            -
            the +rack+ gem, and will unveil the +mail+ gem if the +Mail+ constant
         
     | 
| 
       247 
     | 
    
         
            -
            is defined.
         
     | 
| 
      
 268 
     | 
    
         
            +
            As you'll find out if you try to run your applications with unveil,
         
     | 
| 
      
 269 
     | 
    
         
            +
            autoload and other forms of runtime requires are the enemy.  Both
         
     | 
| 
      
 270 
     | 
    
         
            +
            unicorn-lockdown and unveiler have support for handling common autoloaded
         
     | 
| 
      
 271 
     | 
    
         
            +
            constants in the rack and mail gems.  If you use other gems that use
         
     | 
| 
      
 272 
     | 
    
         
            +
            autoload or runtime requires, you'll have to add unveils for the appropriate
         
     | 
| 
      
 273 
     | 
    
         
            +
            gems:
         
     | 
| 
       248 
274 
     | 
    
         | 
| 
       249 
275 
     | 
    
         
             
              Unicorn.lockdown(self,
         
     | 
| 
       250 
     | 
    
         
            -
                 
     | 
| 
       251 
     | 
    
         
            -
                :user=>"user_name", # Set application user here
         
     | 
| 
       252 
     | 
    
         
            -
                :pledge=>'rpath prot_exec inet unix', # More may be needed
         
     | 
| 
      
 276 
     | 
    
         
            +
                # ...
         
     | 
| 
       253 
277 
     | 
    
         
             
                :unveil=>{
         
     | 
| 
       254 
278 
     | 
    
         
             
                  'views' => 'r',
         
     | 
| 
       255 
279 
     | 
    
         
             
                  'gem-name' => :gem,
         
     | 
    
        data/bin/unicorn-lockdown-add
    CHANGED
    
    | 
         @@ -19,7 +19,7 @@ owner_uid = nil 
     | 
|
| 
       19 
19 
     | 
    
         
             
            owner_gid = nil
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
            options = OptionParser.new do |opts|
         
     | 
| 
       22 
     | 
    
         
            -
              opts.banner = "Usage: unicorn-lockdown-add [options] app_name"
         
     | 
| 
      
 22 
     | 
    
         
            +
              opts.banner = "Usage: unicorn-lockdown-add -o owner -u user [options] app_name"
         
     | 
| 
       23 
23 
     | 
    
         
             
              opts.separator "Options:"
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
25 
     | 
    
         
             
              opts.on_tail("-h", "-?", "--help", "Show this message") do
         
     | 
| 
         @@ -27,39 +27,44 @@ options = OptionParser.new do |opts| 
     | 
|
| 
       27 
27 
     | 
    
         
             
                exit
         
     | 
| 
       28 
28 
     | 
    
         
             
              end
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
              opts.on("-c  
     | 
| 
      
 30 
     | 
    
         
            +
              opts.on("-c RACKUP_FILE", "rackup configuration file") do |v|
         
     | 
| 
       31 
31 
     | 
    
         
             
                rackup = "rackup_file=#{v}\n"
         
     | 
| 
       32 
32 
     | 
    
         
             
              end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
              opts.on("-d  
     | 
| 
      
 34 
     | 
    
         
            +
              opts.on("-d DIR", "application directory name") do |v|
         
     | 
| 
       35 
35 
     | 
    
         
             
                dir = v
         
     | 
| 
       36 
36 
     | 
    
         
             
              end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
              opts.on("-f  
     | 
| 
      
 38 
     | 
    
         
            +
              opts.on("-f UNICORN_FILE", "unicorn configuration file relative to application directory") do |v|
         
     | 
| 
       39 
39 
     | 
    
         
             
                unicorn_file = v
         
     | 
| 
       40 
40 
     | 
    
         
             
                unicorn = "unicorn_conf=#{v}\n"
         
     | 
| 
       41 
41 
     | 
    
         
             
              end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
     | 
    
         
            -
              opts.on("-o  
     | 
| 
      
 43 
     | 
    
         
            +
              opts.on("-o OWNER", "operating system application owner") do |v|
         
     | 
| 
       44 
44 
     | 
    
         
             
                owner = v
         
     | 
| 
       45 
45 
     | 
    
         
             
                ent = Etc.getpwnam(v)
         
     | 
| 
       46 
46 
     | 
    
         
             
                owner_uid = ent.uid
         
     | 
| 
       47 
47 
     | 
    
         
             
                owner_gid = ent.gid
         
     | 
| 
       48 
48 
     | 
    
         
             
              end
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
              opts.on("-u  
     | 
| 
      
 50 
     | 
    
         
            +
              opts.on("-u USER", "operating system user to run application") do |v|
         
     | 
| 
       51 
51 
     | 
    
         
             
                user = v
         
     | 
| 
       52 
52 
     | 
    
         
             
              end
         
     | 
| 
       53 
53 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
              opts.on("--uid  
     | 
| 
      
 54 
     | 
    
         
            +
              opts.on("--uid UID", "user id to use if creating the user when -U is specified") do |v|
         
     | 
| 
       55 
55 
     | 
    
         
             
                new_user_uid = Integer(v, 10)
         
     | 
| 
       56 
56 
     | 
    
         
             
              end
         
     | 
| 
       57 
57 
     | 
    
         
             
            end
         
     | 
| 
       58 
58 
     | 
    
         
             
            options.parse!
         
     | 
| 
       59 
59 
     | 
    
         | 
| 
      
 60 
     | 
    
         
            +
            unless user && owner
         
     | 
| 
      
 61 
     | 
    
         
            +
              $stderr.puts "Must pass -o and -u options when calling unicorn_lockdown_add"
         
     | 
| 
      
 62 
     | 
    
         
            +
              puts options
         
     | 
| 
      
 63 
     | 
    
         
            +
              exit(1)
         
     | 
| 
      
 64 
     | 
    
         
            +
            end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
       60 
66 
     | 
    
         
             
            app = ARGV.shift
         
     | 
| 
       61 
67 
     | 
    
         
             
            dir ||= app
         
     | 
| 
       62 
     | 
    
         
            -
            base_dir = dir
         
     | 
| 
       63 
68 
     | 
    
         | 
| 
       64 
69 
     | 
    
         
             
            root_id = 0
         
     | 
| 
       65 
70 
     | 
    
         
             
            bin_id = 7
         
     | 
| 
         @@ -67,72 +72,51 @@ www_id = 67 
     | 
|
| 
       67 
72 
     | 
    
         | 
| 
       68 
73 
     | 
    
         
             
            www_root = '/var/www'
         
     | 
| 
       69 
74 
     | 
    
         
             
            dir = "#{www_root}/#{dir}"
         
     | 
| 
       70 
     | 
    
         
            -
            etc_dir = "#{dir}/etc"
         
     | 
| 
       71 
     | 
    
         
            -
            hosts_file = "#{etc_dir}/hosts"
         
     | 
| 
       72 
     | 
    
         
            -
            resolv_file = "#{etc_dir}/resolv.conf"
         
     | 
| 
       73 
75 
     | 
    
         
             
            rc_file = "/etc/rc.d/unicorn_#{app.tr('-', '_')}"
         
     | 
| 
       74 
76 
     | 
    
         
             
            nginx_file = "/etc/nginx/#{app}.conf"
         
     | 
| 
       75 
77 
     | 
    
         
             
            unicorn_conf_file = "#{dir}/#{unicorn_file}"
         
     | 
| 
      
 78 
     | 
    
         
            +
            nonroot_dir = "/var/www/request-error-data/#{app}"
         
     | 
| 
       76 
79 
     | 
    
         
             
            unicorn_log_file = "/var/log/unicorn/#{app}.log"
         
     | 
| 
       77 
80 
     | 
    
         
             
            nginx_access_log_file = "/var/log/nginx/#{app}.access.log"
         
     | 
| 
       78 
81 
     | 
    
         
             
            nginx_error_log_file = "/var/log/nginx/#{app}.error.log"
         
     | 
| 
       79 
82 
     | 
    
         | 
| 
       80 
83 
     | 
    
         
             
            # Add application user if it doesn't exist
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
               
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
               
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                 
     | 
| 
       87 
     | 
    
         
            -
                  args << '-u' << new_user_uid.to_s
         
     | 
| 
       88 
     | 
    
         
            -
                end
         
     | 
| 
       89 
     | 
    
         
            -
                args << user
         
     | 
| 
       90 
     | 
    
         
            -
                sh(*args)
         
     | 
| 
       91 
     | 
    
         
            -
                Etc.getpwnam(user)
         
     | 
| 
      
 84 
     | 
    
         
            +
            passwd = begin
         
     | 
| 
      
 85 
     | 
    
         
            +
              Etc.getpwnam(user)
         
     | 
| 
      
 86 
     | 
    
         
            +
            rescue ArgumentError
         
     | 
| 
      
 87 
     | 
    
         
            +
              args = ['/usr/sbin/useradd', '-d', '/var/empty', '-g', '=uid', '-G', '_unicorn', '-L', 'daemon', '-s', '/sbin/nologin']
         
     | 
| 
      
 88 
     | 
    
         
            +
              if new_user_uid
         
     | 
| 
      
 89 
     | 
    
         
            +
                args << '-u' << new_user_uid.to_s
         
     | 
| 
       92 
90 
     | 
    
         
             
              end
         
     | 
| 
       93 
     | 
    
         
            -
               
     | 
| 
      
 91 
     | 
    
         
            +
              args << user
         
     | 
| 
      
 92 
     | 
    
         
            +
              sh(*args)
         
     | 
| 
      
 93 
     | 
    
         
            +
              Etc.getpwnam(user)
         
     | 
| 
      
 94 
     | 
    
         
            +
            end
         
     | 
| 
      
 95 
     | 
    
         
            +
            app_uid = passwd.uid
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
            # Create the subdirectory used for request error info when not running as root
         
     | 
| 
      
 98 
     | 
    
         
            +
            unless File.directory?(nonroot_dir)
         
     | 
| 
      
 99 
     | 
    
         
            +
              puts "Creating #{nonroot_dir}"
         
     | 
| 
      
 100 
     | 
    
         
            +
              Dir.mkdir(nonroot_dir)
         
     | 
| 
      
 101 
     | 
    
         
            +
              File.chmod(0700, nonroot_dir)
         
     | 
| 
      
 102 
     | 
    
         
            +
              File.chown(app_uid, app_uid, nonroot_dir)
         
     | 
| 
       94 
103 
     | 
    
         
             
            end
         
     | 
| 
       95 
104 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
            # Create application directory  
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
      
 105 
     | 
    
         
            +
            # Create application public directory if it doesn't exist (needed by nginx)
         
     | 
| 
      
 106 
     | 
    
         
            +
            dirs = [dir, "#{dir}/public"]
         
     | 
| 
      
 107 
     | 
    
         
            +
            dirs.each do |d|
         
     | 
| 
       98 
108 
     | 
    
         
             
              unless File.directory?(d)
         
     | 
| 
       99 
109 
     | 
    
         
             
                puts "Creating #{d}"
         
     | 
| 
       100 
110 
     | 
    
         
             
                Dir.mkdir(d)
         
     | 
| 
       101 
111 
     | 
    
         
             
                File.chmod(0755, d)
         
     | 
| 
       102 
     | 
    
         
            -
                File.chown(owner_uid, owner_gid, d) 
     | 
| 
      
 112 
     | 
    
         
            +
                File.chown(owner_uid, owner_gid, d)
         
     | 
| 
       103 
113 
     | 
    
         
             
              end
         
     | 
| 
       104 
114 
     | 
    
         
             
            end
         
     | 
| 
       105 
115 
     | 
    
         | 
| 
       106 
116 
     | 
    
         
             
            # DRY up file ownership code
         
     | 
| 
       107 
117 
     | 
    
         
             
            setup_file_owner = lambda do |file|
         
     | 
| 
       108 
118 
     | 
    
         
             
              File.chmod(0644, file)
         
     | 
| 
       109 
     | 
    
         
            -
              File.chown(owner_uid, owner_gid, file) 
     | 
| 
       110 
     | 
    
         
            -
            end
         
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
       112 
     | 
    
         
            -
            # Setup symlink to root so that the same paths work both when
         
     | 
| 
       113 
     | 
    
         
            -
            # chrooted and when not chrooted.
         
     | 
| 
       114 
     | 
    
         
            -
            chroot_link = "#{dir}#{dir}"
         
     | 
| 
       115 
     | 
    
         
            -
            unless File.symlink?(chroot_link)
         
     | 
| 
       116 
     | 
    
         
            -
              puts "Creating #{chroot_link}"
         
     | 
| 
       117 
     | 
    
         
            -
              File.symlink('/', chroot_link)
         
     | 
| 
       118 
     | 
    
         
            -
            end
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
            # Add /etc/hosts files to chroot
         
     | 
| 
       121 
     | 
    
         
            -
            unless File.file?(hosts_file)
         
     | 
| 
       122 
     | 
    
         
            -
              puts "Creating #{hosts_file}"
         
     | 
| 
       123 
     | 
    
         
            -
              File.binwrite(hosts_file, <<END)
         
     | 
| 
       124 
     | 
    
         
            -
            127.0.0.1 localhost
         
     | 
| 
       125 
     | 
    
         
            -
            END
         
     | 
| 
       126 
     | 
    
         
            -
              setup_file_owner.call(hosts_file)
         
     | 
| 
       127 
     | 
    
         
            -
            end
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
            # Add /etc/resolv.conf files to chroot
         
     | 
| 
       130 
     | 
    
         
            -
            unless File.file?(resolv_file)
         
     | 
| 
       131 
     | 
    
         
            -
              puts "Creating #{resolv_file}"
         
     | 
| 
       132 
     | 
    
         
            -
              File.binwrite(resolv_file, <<END)
         
     | 
| 
       133 
     | 
    
         
            -
            lookup file
         
     | 
| 
       134 
     | 
    
         
            -
            END
         
     | 
| 
       135 
     | 
    
         
            -
              setup_file_owner.call(resolv_file)
         
     | 
| 
      
 119 
     | 
    
         
            +
              File.chown(owner_uid, owner_gid, file)
         
     | 
| 
       136 
120 
     | 
    
         
             
            end
         
     | 
| 
       137 
121 
     | 
    
         | 
| 
       138 
122 
     | 
    
         
             
            # Setup unicorn configuration file
         
     | 
| 
         @@ -142,7 +126,7 @@ unless File.file?(unicorn_conf_file) 
     | 
|
| 
       142 
126 
     | 
    
         
             
                puts "Creating #{unicorn_conf_dir}"
         
     | 
| 
       143 
127 
     | 
    
         
             
                Dir.mkdir(unicorn_conf_dir)
         
     | 
| 
       144 
128 
     | 
    
         
             
                File.chmod(0755, unicorn_conf_dir)
         
     | 
| 
       145 
     | 
    
         
            -
                File.chown(owner_uid, owner_gid, unicorn_conf_dir) 
     | 
| 
      
 129 
     | 
    
         
            +
                File.chown(owner_uid, owner_gid, unicorn_conf_dir)
         
     | 
| 
       146 
130 
     | 
    
         
             
              end
         
     | 
| 
       147 
131 
     | 
    
         
             
              puts "Creating #{unicorn_conf_file}"
         
     | 
| 
       148 
132 
     | 
    
         
             
              File.binwrite(unicorn_conf_file, <<END)
         
     | 
| 
         @@ -150,9 +134,22 @@ require 'unicorn-lockdown' 
     | 
|
| 
       150 
134 
     | 
    
         | 
| 
       151 
135 
     | 
    
         
             
            Unicorn.lockdown(self,
         
     | 
| 
       152 
136 
     | 
    
         
             
              :app=>#{app.inspect},
         
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
               
     | 
| 
       155 
     | 
    
         
            -
              :email=>'root' 
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
              # Update this with correct email
         
     | 
| 
      
 139 
     | 
    
         
            +
              :email=>'root',
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
              # More pledges may be needed depending on application
         
     | 
| 
      
 142 
     | 
    
         
            +
              :pledge=>'rpath prot_exec inet unix',
         
     | 
| 
      
 143 
     | 
    
         
            +
              :master_pledge=>'rpath prot_exec cpath wpath inet proc exec',
         
     | 
| 
      
 144 
     | 
    
         
            +
              :master_execpledge=>'stdio rpath prot_exec inet unix cpath wpath unveil',
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
              # More unveils may be needed depending on application
         
     | 
| 
      
 147 
     | 
    
         
            +
              :unveil=>{
         
     | 
| 
      
 148 
     | 
    
         
            +
                'views'=>'r'
         
     | 
| 
      
 149 
     | 
    
         
            +
              },
         
     | 
| 
      
 150 
     | 
    
         
            +
              :dev_unveil=>{
         
     | 
| 
      
 151 
     | 
    
         
            +
                'models'=>'r'
         
     | 
| 
      
 152 
     | 
    
         
            +
              },
         
     | 
| 
       156 
153 
     | 
    
         
             
            )
         
     | 
| 
       157 
154 
     | 
    
         
             
            END
         
     | 
| 
       158 
155 
     | 
    
         
             
              setup_file_owner.call(unicorn_conf_file)
         
     | 
| 
         @@ -204,7 +201,7 @@ unless File.file?(unicorn_log_file) 
     | 
|
| 
       204 
201 
     | 
    
         
             
              puts "Creating #{unicorn_log_file}"
         
     | 
| 
       205 
202 
     | 
    
         
             
              File.binwrite(unicorn_log_file, '')
         
     | 
| 
       206 
203 
     | 
    
         
             
              File.chmod(0640, unicorn_log_file)
         
     | 
| 
       207 
     | 
    
         
            -
              File.chown(app_uid,  
     | 
| 
      
 204 
     | 
    
         
            +
              File.chown(app_uid, Etc.getgrnam('_unicorn').gid, unicorn_log_file) if app_uid
         
     | 
| 
       208 
205 
     | 
    
         
             
            end
         
     | 
| 
       209 
206 
     | 
    
         | 
| 
       210 
207 
     | 
    
         
             
            # Setup /etc/rc.d/unicorn_* file for daemon management
         
     | 
| 
         @@ -213,6 +210,7 @@ unless File.file?(rc_file) 
     | 
|
| 
       213 
210 
     | 
    
         
             
              File.binwrite(rc_file, <<END)
         
     | 
| 
       214 
211 
     | 
    
         
             
            #!/bin/ksh
         
     | 
| 
       215 
212 
     | 
    
         | 
| 
      
 213 
     | 
    
         
            +
            daemon_user=#{user}
         
     | 
| 
       216 
214 
     | 
    
         
             
            unicorn_app=#{app}
         
     | 
| 
       217 
215 
     | 
    
         
             
            unicorn_dir=#{dir}
         
     | 
| 
       218 
216 
     | 
    
         
             
            #{unicorn}#{rackup} 
         
     | 
    
        data/bin/unicorn-lockdown-setup
    CHANGED
    
    | 
         @@ -7,7 +7,7 @@ def sh(*args) 
     | 
|
| 
       7 
7 
     | 
    
         
             
              system(*args) || raise("Error while running: #{args.join(' ')}")
         
     | 
| 
       8 
8 
     | 
    
         
             
            end
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            request_dir = '/var/www/ 
     | 
| 
      
 10 
     | 
    
         
            +
            request_dir = '/var/www/request-error-data'
         
     | 
| 
       11 
11 
     | 
    
         
             
            socket_dir = '/var/www/sockets'
         
     | 
| 
       12 
12 
     | 
    
         
             
            unicorn_log_dir = '/var/log/unicorn'
         
     | 
| 
       13 
13 
     | 
    
         
             
            nginx_log_dir = "/var/log/nginx"
         
     | 
| 
         @@ -31,11 +31,11 @@ unicorn_group_id = group.gid 
     | 
|
| 
       31 
31 
     | 
    
         
             
            unless File.directory?(request_dir)
         
     | 
| 
       32 
32 
     | 
    
         
             
              puts "Creating #{request_dir}"
         
     | 
| 
       33 
33 
     | 
    
         
             
              Dir.mkdir(request_dir)
         
     | 
| 
       34 
     | 
    
         
            -
              File.chmod( 
     | 
| 
       35 
     | 
    
         
            -
              File.chown(root_id,  
     | 
| 
      
 34 
     | 
    
         
            +
              File.chmod(0710, request_dir)
         
     | 
| 
      
 35 
     | 
    
         
            +
              File.chown(root_id, unicorn_group_id, request_dir)
         
     | 
| 
       36 
36 
     | 
    
         
             
            end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
            # Setup sockets directory to hold  
     | 
| 
      
 38 
     | 
    
         
            +
            # Setup sockets directory to hold nginx connection sockets
         
     | 
| 
       39 
39 
     | 
    
         
             
            unless File.directory?(socket_dir)
         
     | 
| 
       40 
40 
     | 
    
         
             
              puts "Creating #{socket_dir}"
         
     | 
| 
       41 
41 
     | 
    
         
             
              Dir.mkdir(socket_dir)
         
     | 
| 
         @@ -8,10 +8,9 @@ class Roda 
     | 
|
| 
       8 
8 
     | 
    
         
             
                # the QUIT signal, allowing Unicorn to finish handling the current request before exiting.
         
     | 
| 
       9 
9 
     | 
    
         
             
                #
         
     | 
| 
       10 
10 
     | 
    
         
             
                # This is designed to be used with applications that cannot connect to the database
         
     | 
| 
       11 
     | 
    
         
            -
                # after application initialization, either because they are  
     | 
| 
       12 
     | 
    
         
            -
                #  
     | 
| 
       13 
     | 
    
         
            -
                #  
     | 
| 
       14 
     | 
    
         
            -
                # privileges are dropped.
         
     | 
| 
      
 11 
     | 
    
         
            +
                # after application initialization, either because they are restricting access to the database
         
     | 
| 
      
 12 
     | 
    
         
            +
                # socket using unveil, or because they are using a firewall and access to the database server is
         
     | 
| 
      
 13 
     | 
    
         
            +
                # not allowed from the application the process is running as after privileges are dropped.
         
     | 
| 
       15 
14 
     | 
    
         
             
                # 
         
     | 
| 
       16 
15 
     | 
    
         
             
                # This plugin must be loaded before the roda error_handler plugin, and it assumes usage of the
         
     | 
| 
       17 
16 
     | 
    
         
             
                # Sequel database library with the postgres adapter and pg driver.
         
     | 
| 
         @@ -23,7 +22,7 @@ class Roda 
     | 
|
| 
       23 
22 
     | 
    
         
             
                  module InstanceMethods
         
     | 
| 
       24 
23 
     | 
    
         
             
                    # When database connection is lost, kill the worker process, so a new one will be generated.
         
     | 
| 
       25 
24 
     | 
    
         
             
                    # This is necessary because the unix socket used by the database connection is no longer available
         
     | 
| 
       26 
     | 
    
         
            -
                    # once the application is  
     | 
| 
      
 25 
     | 
    
         
            +
                    # once the application is unveiled or pledged.
         
     | 
| 
       27 
26 
     | 
    
         
             
                    def call
         
     | 
| 
       28 
27 
     | 
    
         
             
                      super
         
     | 
| 
       29 
28 
     | 
    
         
             
                    rescue Sequel::DatabaseDisconnectError, Sequel::DatabaseConnectionError, PG::ConnectionBad
         
     | 
| 
         @@ -33,7 +32,7 @@ class Roda 
     | 
|
| 
       33 
32 
     | 
    
         | 
| 
       34 
33 
     | 
    
         
             
                    # When database connection is lost, kill the worker process, so a new one will be generated.
         
     | 
| 
       35 
34 
     | 
    
         
             
                    # This is necessary because the unix socket used by the database connection is no longer available
         
     | 
| 
       36 
     | 
    
         
            -
                    # once the application is  
     | 
| 
      
 35 
     | 
    
         
            +
                    # once the application is unveiled or pledged.
         
     | 
| 
       37 
36 
     | 
    
         
             
                    def _roda_handle_main_route
         
     | 
| 
       38 
37 
     | 
    
         
             
                      super
         
     | 
| 
       39 
38 
     | 
    
         
             
                    rescue Sequel::DatabaseDisconnectError, Sequel::DatabaseConnectionError, PG::ConnectionBad
         
     | 
    
        data/lib/unicorn-lockdown.rb
    CHANGED
    
    | 
         @@ -1,25 +1,38 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # unicorn-lockdown is designed to  
     | 
| 
       2 
     | 
    
         
            -
            #  
     | 
| 
      
 1 
     | 
    
         
            +
            # unicorn-lockdown is designed to handle fork+exec, unveil, and pledge support
         
     | 
| 
      
 2 
     | 
    
         
            +
            # when using Unicorn, including:
         
     | 
| 
      
 3 
     | 
    
         
            +
            # * restricting file system access using unveil
         
     | 
| 
       3 
4 
     | 
    
         
             
            # * pledging the app to restrict allowed syscalls at the appropriate point
         
     | 
| 
       4 
     | 
    
         
            -
            # * handling notifications of worker crashes
         
     | 
| 
       5 
     | 
    
         
            -
            # 
     | 
| 
       6 
     | 
    
         
            -
            # * stripping path prefixes from the reloader in development mode
         
     | 
| 
      
 5 
     | 
    
         
            +
            # * handling notifications of worker crashes (which are likely due to pledge
         
     | 
| 
      
 6 
     | 
    
         
            +
            #   violations)
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            require 'pledge'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'unveil'
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            #  
     | 
| 
      
 11 
     | 
    
         
            +
            # Load common encodings
         
     | 
| 
       11 
12 
     | 
    
         
             
            "\255".force_encoding('ISO8859-1').encode('UTF-8')
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
            # Load encodings
         
     | 
| 
       14 
13 
     | 
    
         
             
            ''.force_encoding('UTF-16LE')
         
     | 
| 
       15 
14 
     | 
    
         
             
            ''.force_encoding('UTF-16BE')
         
     | 
| 
       16 
15 
     | 
    
         | 
| 
       17 
16 
     | 
    
         
             
            class Unicorn::HttpServer
         
     | 
| 
       18 
     | 
    
         
            -
              # The file name in which to store request information. 
     | 
| 
       19 
     | 
    
         
            -
              # /var/www/ 
     | 
| 
       20 
     | 
    
         
            -
              # to  
     | 
| 
      
 17 
     | 
    
         
            +
              # The file name in which to store request information.
         
     | 
| 
      
 18 
     | 
    
         
            +
              # The /var/www/request-error-data/$app_name folder is accessable
         
     | 
| 
      
 19 
     | 
    
         
            +
              # only to the user of the application.
         
     | 
| 
       21 
20 
     | 
    
         
             
              def request_filename(pid)
         
     | 
| 
       22 
     | 
    
         
            -
                "/var/www/ 
     | 
| 
      
 21 
     | 
    
         
            +
                "/var/www/request-error-data/#{Unicorn.app_name}/#{pid}.txt"
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              unless ENV['UNICORN_WORKER']
         
     | 
| 
      
 25 
     | 
    
         
            +
                alias _original_spawn_missing_workers spawn_missing_workers
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                # This is the master process, set the master pledge before spawning
         
     | 
| 
      
 28 
     | 
    
         
            +
                # workers, because spawning workers will also need to be done at runtime.
         
     | 
| 
      
 29 
     | 
    
         
            +
                def spawn_missing_workers
         
     | 
| 
      
 30 
     | 
    
         
            +
                  if pledge = Unicorn.master_pledge
         
     | 
| 
      
 31 
     | 
    
         
            +
                    Unicorn.master_pledge = nil
         
     | 
| 
      
 32 
     | 
    
         
            +
                    Pledge.pledge(pledge, Unicorn.master_execpledge)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  _original_spawn_missing_workers
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
       23 
36 
     | 
    
         
             
              end
         
     | 
| 
       24 
37 
     | 
    
         | 
| 
       25 
38 
     | 
    
         
             
              # Override the process name for the unicorn processes, both master and
         
     | 
| 
         @@ -49,14 +62,13 @@ class << Unicorn 
     | 
|
| 
       49 
62 
     | 
    
         
             
              # to enable programmers to debug and fix the issue.
         
     | 
| 
       50 
63 
     | 
    
         
             
              attr_accessor :request_logger
         
     | 
| 
       51 
64 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
              # The  
     | 
| 
       53 
     | 
    
         
            -
              attr_accessor : 
     | 
| 
      
 65 
     | 
    
         
            +
              # The pledge string to use for the master process's spawned processes by default.
         
     | 
| 
      
 66 
     | 
    
         
            +
              attr_accessor :master_execpledge
         
     | 
| 
       54 
67 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
              # The  
     | 
| 
       56 
     | 
    
         
            -
               
     | 
| 
       57 
     | 
    
         
            -
              attr_accessor :group_name
         
     | 
| 
      
 68 
     | 
    
         
            +
              # The pledge string to use for the master process.
         
     | 
| 
      
 69 
     | 
    
         
            +
              attr_accessor :master_pledge
         
     | 
| 
       58 
70 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
              # The pledge string to use.
         
     | 
| 
      
 71 
     | 
    
         
            +
              # The pledge string to use for worker processes.
         
     | 
| 
       60 
72 
     | 
    
         
             
              attr_accessor :pledge
         
     | 
| 
       61 
73 
     | 
    
         | 
| 
       62 
74 
     | 
    
         
             
              # The hash of unveil paths to use.
         
     | 
| 
         @@ -65,13 +77,13 @@ class << Unicorn 
     | 
|
| 
       65 
77 
     | 
    
         
             
              # The hash of additional unveil paths to use if in the development environment.
         
     | 
| 
       66 
78 
     | 
    
         
             
              attr_accessor :dev_unveil
         
     | 
| 
       67 
79 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
              # The address to email for crash and unhandled exception notifications
         
     | 
| 
      
 80 
     | 
    
         
            +
              # The address to email for crash and unhandled exception notifications.
         
     | 
| 
       69 
81 
     | 
    
         
             
              attr_accessor :email
         
     | 
| 
       70 
82 
     | 
    
         | 
| 
       71 
83 
     | 
    
         
             
              # Helper method to write request information to the request logger.
         
     | 
| 
       72 
84 
     | 
    
         
             
              # +email_message+ should be an email message including headers and body.
         
     | 
| 
       73 
85 
     | 
    
         
             
              # This should be called at the top of the Roda route block for the
         
     | 
| 
       74 
     | 
    
         
            -
              # application.
         
     | 
| 
      
 86 
     | 
    
         
            +
              # application (or at some early point before processing in other web frameworks).
         
     | 
| 
       75 
87 
     | 
    
         
             
              def write_request(email_message)
         
     | 
| 
       76 
88 
     | 
    
         
             
                request_logger.seek(0, IO::SEEK_SET)
         
     | 
| 
       77 
89 
     | 
    
         
             
                request_logger.truncate(0)
         
     | 
| 
         @@ -79,26 +91,27 @@ class << Unicorn 
     | 
|
| 
       79 
91 
     | 
    
         
             
                request_logger.fsync
         
     | 
| 
       80 
92 
     | 
    
         
             
              end
         
     | 
| 
       81 
93 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
              # Helper method that sets up all necessary code for  
     | 
| 
      
 94 
     | 
    
         
            +
              # Helper method that sets up all necessary code for unveil/pledge support.
         
     | 
| 
       83 
95 
     | 
    
         
             
              # This should be called inside the appropriate unicorn.conf file.
         
     | 
| 
       84 
96 
     | 
    
         
             
              # The configurator should be self in the top level scope of the
         
     | 
| 
       85 
97 
     | 
    
         
             
              # unicorn.conf file, and this takes options:
         
     | 
| 
       86 
98 
     | 
    
         
             
              #
         
     | 
| 
       87 
99 
     | 
    
         
             
              # Options:
         
     | 
| 
       88 
     | 
    
         
            -
              # :app :: The name of the application 
     | 
| 
      
 100 
     | 
    
         
            +
              # :app (required) :: The name of the application
         
     | 
| 
       89 
101 
     | 
    
         
             
              # :email : The email to notify for worker crashes
         
     | 
| 
       90 
     | 
    
         
            -
              # : 
     | 
| 
       91 
     | 
    
         
            -
              # : 
     | 
| 
       92 
     | 
    
         
            -
              # 
     | 
| 
       93 
     | 
    
         
            -
              # 
     | 
| 
       94 
     | 
    
         
            -
              #  
     | 
| 
       95 
     | 
    
         
            -
              # :unveil :: A hash of unveil paths
         
     | 
| 
       96 
     | 
    
         
            -
              # :dev_unveil :: A hash of unveil paths to use in development
         
     | 
| 
      
 102 
     | 
    
         
            +
              # :pledge :: The string to use when pledging worker processes after loading the app
         
     | 
| 
      
 103 
     | 
    
         
            +
              # :master_pledge :: The string to use when pledging the master process before
         
     | 
| 
      
 104 
     | 
    
         
            +
              #                   spawning worker processes
         
     | 
| 
      
 105 
     | 
    
         
            +
              # :master_execpledge :: The pledge string for processes spawned by the master
         
     | 
| 
      
 106 
     | 
    
         
            +
              #                       process (i.e. worker processes before loading the app)
         
     | 
| 
      
 107 
     | 
    
         
            +
              # :unveil :: A hash of unveil paths, passed to Pledge.unveil.
         
     | 
| 
      
 108 
     | 
    
         
            +
              # :dev_unveil :: A hash of unveil paths to use in development, in addition
         
     | 
| 
      
 109 
     | 
    
         
            +
              #                to the ones in :unveil.
         
     | 
| 
       97 
110 
     | 
    
         
             
              def lockdown(configurator, opts)
         
     | 
| 
       98 
111 
     | 
    
         
             
                Unicorn.app_name = opts.fetch(:app)
         
     | 
| 
       99 
     | 
    
         
            -
                Unicorn.user_name = opts.fetch(:user)
         
     | 
| 
       100 
     | 
    
         
            -
                Unicorn.group_name = opts[:group] || opts[:user]
         
     | 
| 
       101 
112 
     | 
    
         
             
                Unicorn.email = opts[:email]
         
     | 
| 
      
 113 
     | 
    
         
            +
                Unicorn.master_pledge = opts[:master_pledge]
         
     | 
| 
      
 114 
     | 
    
         
            +
                Unicorn.master_execpledge = opts[:master_execpledge]
         
     | 
| 
       102 
115 
     | 
    
         
             
                Unicorn.pledge = opts[:pledge]
         
     | 
| 
       103 
116 
     | 
    
         
             
                Unicorn.unveil = opts[:unveil]
         
     | 
| 
       104 
117 
     | 
    
         
             
                Unicorn.dev_unveil = opts[:dev_unveil]
         
     | 
| 
         @@ -125,13 +138,12 @@ class << Unicorn 
     | 
|
| 
       125 
138 
     | 
    
         
             
                  after_fork do |server, worker|
         
     | 
| 
       126 
139 
     | 
    
         
             
                    server.logger.info("worker=#{worker.nr} spawned pid=#{$$}")
         
     | 
| 
       127 
140 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
                    # Set the request logger for the worker process after forking. 
     | 
| 
       129 
     | 
    
         
            -
                    # process is still root here, so it can open the file in write mode.
         
     | 
| 
      
 141 
     | 
    
         
            +
                    # Set the request logger for the worker process after forking.
         
     | 
| 
       130 
142 
     | 
    
         
             
                    Unicorn.request_logger = File.open(server.request_filename($$), "wb")
         
     | 
| 
       131 
143 
     | 
    
         
             
                    Unicorn.request_logger.sync = true
         
     | 
| 
       132 
144 
     | 
    
         
             
                  end
         
     | 
| 
       133 
145 
     | 
    
         | 
| 
       134 
     | 
    
         
            -
                  if wrap_app = Unicorn.email 
     | 
| 
      
 146 
     | 
    
         
            +
                  if wrap_app = Unicorn.email
         
     | 
| 
       135 
147 
     | 
    
         
             
                    require 'rack/email_exceptions'
         
     | 
| 
       136 
148 
     | 
    
         
             
                  end
         
     | 
| 
       137 
149 
     | 
    
         | 
| 
         @@ -147,80 +159,31 @@ class << Unicorn 
     | 
|
| 
       147 
159 
     | 
    
         
             
                      end
         
     | 
| 
       148 
160 
     | 
    
         
             
                    end
         
     | 
| 
       149 
161 
     | 
    
         | 
| 
       150 
     | 
    
         
            -
                     
     | 
| 
       151 
     | 
    
         
            -
                       
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                       
     | 
| 
       154 
     | 
    
         
            -
             
     | 
| 
       155 
     | 
    
         
            -
                      else
         
     | 
| 
       156 
     | 
    
         
            -
                        Hash[unveil]
         
     | 
| 
       157 
     | 
    
         
            -
                      end
         
     | 
| 
      
 162 
     | 
    
         
            +
                    unveil = if Unicorn.dev_unveil && ENV['RACK_ENV'] == 'development'
         
     | 
| 
      
 163 
     | 
    
         
            +
                      Unicorn.unveil.merge(Unicorn.dev_unveil)
         
     | 
| 
      
 164 
     | 
    
         
            +
                    else
         
     | 
| 
      
 165 
     | 
    
         
            +
                      Hash[Unicorn.unveil]
         
     | 
| 
      
 166 
     | 
    
         
            +
                    end
         
     | 
| 
       158 
167 
     | 
    
         | 
| 
      
 168 
     | 
    
         
            +
                    # Don't allow loading files in rack and mail gems if not using rubygems
         
     | 
| 
      
 169 
     | 
    
         
            +
                    if defined?(Gem) && Gem.respond_to?(:loaded_specs)
         
     | 
| 
       159 
170 
     | 
    
         
             
                      # Allow read access to the rack gem directory, as rack autoloads constants.
         
     | 
| 
       160 
     | 
    
         
            -
                       
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
                      if defined?(Mail)
         
     | 
| 
       163 
     | 
    
         
            -
                        # If using the mail library, allow read access to the mail gem directory,
         
     | 
| 
       164 
     | 
    
         
            -
                        # as mail autoloads constants.
         
     | 
| 
       165 
     | 
    
         
            -
                        unveil['mail'] = :gem
         
     | 
| 
       166 
     | 
    
         
            -
                      end
         
     | 
| 
       167 
     | 
    
         
            -
             
     | 
| 
       168 
     | 
    
         
            -
                      # Drop privileges
         
     | 
| 
       169 
     | 
    
         
            -
                      worker.user(Unicorn.user_name, Unicorn.group_name)
         
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
       171 
     | 
    
         
            -
                      # Restrict access to the file system based on the specified unveil.
         
     | 
| 
       172 
     | 
    
         
            -
                      Pledge.unveil(unveil)
         
     | 
| 
       173 
     | 
    
         
            -
                    else
         
     | 
| 
       174 
     | 
    
         
            -
                      # Before chrooting, reference all constants that use autoload
         
     | 
| 
       175 
     | 
    
         
            -
                      # that are probably needed at runtime.  This must be done
         
     | 
| 
       176 
     | 
    
         
            -
                      # before chrooting as attempting to load the constants after
         
     | 
| 
       177 
     | 
    
         
            -
                      # chrooting will break things.
         
     | 
| 
       178 
     | 
    
         
            -
                      
         
     | 
| 
       179 
     | 
    
         
            -
                      # Start with rack, which uses autoload for all constants.
         
     | 
| 
       180 
     | 
    
         
            -
                      # Most of rack's constants are not used at runtime, this
         
     | 
| 
       181 
     | 
    
         
            -
                      # lists the ones most commonly needed.
         
     | 
| 
       182 
     | 
    
         
            -
                      Rack::Multipart
         
     | 
| 
       183 
     | 
    
         
            -
                      Rack::Multipart::Parser
         
     | 
| 
       184 
     | 
    
         
            -
                      Rack::Multipart::Generator
         
     | 
| 
       185 
     | 
    
         
            -
                      Rack::Multipart::UploadedFile
         
     | 
| 
       186 
     | 
    
         
            -
                      Rack::Mime
         
     | 
| 
       187 
     | 
    
         
            -
                      Rack::Auth::Digest::Params
         
     | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       189 
     | 
    
         
            -
                      # In the development environment, reference all middleware
         
     | 
| 
       190 
     | 
    
         
            -
                      # the unicorn will load by default, unless unicorn is
         
     | 
| 
       191 
     | 
    
         
            -
                      # set to not load middleware by default.
         
     | 
| 
       192 
     | 
    
         
            -
                      if ENV['RACK_ENV'] == 'development' && (!respond_to?(:set) || set[:default_middleware] != false)
         
     | 
| 
       193 
     | 
    
         
            -
                        Rack::ContentLength
         
     | 
| 
       194 
     | 
    
         
            -
                        Rack::CommonLogger
         
     | 
| 
       195 
     | 
    
         
            -
                        Rack::Chunked
         
     | 
| 
       196 
     | 
    
         
            -
                        Rack::Lint
         
     | 
| 
       197 
     | 
    
         
            -
                        Rack::ShowExceptions
         
     | 
| 
       198 
     | 
    
         
            -
                        Rack::TempfileReaper
         
     | 
| 
      
 171 
     | 
    
         
            +
                      if defined?(Rack) && Gem.loaded_specs['rack']
         
     | 
| 
      
 172 
     | 
    
         
            +
                        unveil['rack'] = :gem
         
     | 
| 
       199 
173 
     | 
    
         
             
                      end
         
     | 
| 
       200 
174 
     | 
    
         | 
| 
       201 
     | 
    
         
            -
                      # If using the mail library,  
     | 
| 
       202 
     | 
    
         
            -
                      #  
     | 
| 
       203 
     | 
    
         
            -
                       
     | 
| 
       204 
     | 
    
         
            -
             
     | 
| 
       205 
     | 
    
         
            -
                      if defined?(Mail)
         
     | 
| 
       206 
     | 
    
         
            -
                        Mail.eager_autoload!
         
     | 
| 
      
 175 
     | 
    
         
            +
                      # If using the mail library, allow read access to the mail gem directory,
         
     | 
| 
      
 176 
     | 
    
         
            +
                      # as mail autoloads constants.
         
     | 
| 
      
 177 
     | 
    
         
            +
                      if defined?(Mail) && Gem.loaded_specs['mail']
         
     | 
| 
      
 178 
     | 
    
         
            +
                        unveil['mail'] = :gem
         
     | 
| 
       207 
179 
     | 
    
         
             
                      end
         
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
                      # Strip path prefixes from the reloader.  This is only
         
     | 
| 
       210 
     | 
    
         
            -
                      # really need in development mode for code reloading to work.
         
     | 
| 
       211 
     | 
    
         
            -
                      pwd = Dir.pwd
         
     | 
| 
       212 
     | 
    
         
            -
                      Unreloader.strip_path_prefix(pwd) if defined?(Unreloader)
         
     | 
| 
       213 
     | 
    
         
            -
             
     | 
| 
       214 
     | 
    
         
            -
                      # Drop privileges.  This must be done after chrooting as
         
     | 
| 
       215 
     | 
    
         
            -
                      # chrooting requires root privileges.
         
     | 
| 
       216 
     | 
    
         
            -
                      worker.user(Unicorn.user_name, Unicorn.group_name, pwd)
         
     | 
| 
       217 
180 
     | 
    
         
             
                    end
         
     | 
| 
       218 
181 
     | 
    
         | 
| 
       219 
     | 
    
         
            -
                     
     | 
| 
       220 
     | 
    
         
            -
             
     | 
| 
       221 
     | 
    
         
            -
             
     | 
| 
       222 
     | 
    
         
            -
             
     | 
| 
       223 
     | 
    
         
            -
                     
     | 
| 
      
 182 
     | 
    
         
            +
                    # Restrict access to the file system based on the specified unveil.
         
     | 
| 
      
 183 
     | 
    
         
            +
                    Pledge.unveil(unveil)
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                    # Pledge after unveiling, because unveiling requires a separate pledge.
         
     | 
| 
      
 186 
     | 
    
         
            +
                    Pledge.pledge(Unicorn.pledge)
         
     | 
| 
       224 
187 
     | 
    
         
             
                  end
         
     | 
| 
       225 
188 
     | 
    
         | 
| 
       226 
189 
     | 
    
         
             
                  # the last time there was a worker crash and the request information
         
     | 
| 
         @@ -263,30 +226,13 @@ class << Unicorn 
     | 
|
| 
       263 
226 
     | 
    
         
             
                          # If the request filename exists and the worker process crashed,
         
     | 
| 
       264 
227 
     | 
    
         
             
                          # send a notification email.
         
     | 
| 
       265 
228 
     | 
    
         
             
                          Process.waitpid(fork do
         
     | 
| 
       266 
     | 
    
         
            -
                            # Load net/smtp early 
     | 
| 
      
 229 
     | 
    
         
            +
                            # Load net/smtp early
         
     | 
| 
       267 
230 
     | 
    
         
             
                            require 'net/smtp'
         
     | 
| 
       268 
231 
     | 
    
         | 
| 
       269 
232 
     | 
    
         
             
                            # When setting the email, first get the contents of the email
         
     | 
| 
       270 
233 
     | 
    
         
             
                            # from the request file.
         
     | 
| 
       271 
234 
     | 
    
         
             
                            body = File.read(file)
         
     | 
| 
       272 
235 
     | 
    
         | 
| 
       273 
     | 
    
         
            -
                            # Then get information from /etc and drop group privileges
         
     | 
| 
       274 
     | 
    
         
            -
                            uid = Etc.getpwnam(Unicorn.user_name).uid
         
     | 
| 
       275 
     | 
    
         
            -
                            group = Unicorn.group_name
         
     | 
| 
       276 
     | 
    
         
            -
                            group = group.first if group.is_a?(Array)
         
     | 
| 
       277 
     | 
    
         
            -
                            gid = Etc.getgrnam(group).gid
         
     | 
| 
       278 
     | 
    
         
            -
                            if gid && Process.egid != gid
         
     | 
| 
       279 
     | 
    
         
            -
                              Process.initgroups(Unicorn.user_name, gid)
         
     | 
| 
       280 
     | 
    
         
            -
                              Process::GID.change_privilege(gid)
         
     | 
| 
       281 
     | 
    
         
            -
                            end
         
     | 
| 
       282 
     | 
    
         
            -
             
     | 
| 
       283 
     | 
    
         
            -
                            # Then chroot
         
     | 
| 
       284 
     | 
    
         
            -
                            Dir.chroot(Dir.pwd)
         
     | 
| 
       285 
     | 
    
         
            -
                            Dir.chdir('/')
         
     | 
| 
       286 
     | 
    
         
            -
             
     | 
| 
       287 
     | 
    
         
            -
                            # Then drop user privileges
         
     | 
| 
       288 
     | 
    
         
            -
                            Process.euid != uid and Process::UID.change_privilege(uid)
         
     | 
| 
       289 
     | 
    
         
            -
             
     | 
| 
       290 
236 
     | 
    
         
             
                            # Then use a restrictive pledge
         
     | 
| 
       291 
237 
     | 
    
         
             
                            Pledge.pledge('inet prot_exec')
         
     | 
| 
       292 
238 
     | 
    
         | 
| 
         @@ -309,4 +255,3 @@ class << Unicorn 
     | 
|
| 
       309 
255 
     | 
    
         
             
                end
         
     | 
| 
       310 
256 
     | 
    
         
             
              end
         
     | 
| 
       311 
257 
     | 
    
         
             
            end
         
     | 
| 
       312 
     | 
    
         
            -
             
     | 
    
        data/lib/unveiler.rb
    ADDED
    
    | 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'pledge'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'unveil'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # Load encodings
         
     | 
| 
      
 5 
     | 
    
         
            +
            "\255".force_encoding('ISO8859-1').encode('UTF-8')
         
     | 
| 
      
 6 
     | 
    
         
            +
            ''.force_encoding('UTF-16LE')
         
     | 
| 
      
 7 
     | 
    
         
            +
            ''.force_encoding('UTF-16BE')
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            # Don't run external diff program for failures
         
     | 
| 
      
 10 
     | 
    
         
            +
            Minitest::Assertions.diff = false if defined?(Minitest::Assertions)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            # Unveiler allows for testing programs using pledge and unveil.
         
     | 
| 
      
 13 
     | 
    
         
            +
            module Unveiler
         
     | 
| 
      
 14 
     | 
    
         
            +
              # Use Pledge.unveil to limit access to the file system based on the
         
     | 
| 
      
 15 
     | 
    
         
            +
              # +unveil+ argument.  Then pledge the process with the given +pledge+
         
     | 
| 
      
 16 
     | 
    
         
            +
              # permissions.  This will automatically unveil the rack and mail gems
         
     | 
| 
      
 17 
     | 
    
         
            +
              # if they are loaded.
         
     | 
| 
      
 18 
     | 
    
         
            +
              def self.pledge_and_unveil(pledge, unveil)
         
     | 
| 
      
 19 
     | 
    
         
            +
                unveil = Hash[unveil]
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                if defined?(Gem) && Gem.respond_to?(:loaded_specs)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  if defined?(Rack) && Gem.loaded_specs['rack']
         
     | 
| 
      
 23 
     | 
    
         
            +
                    unveil['rack'] = :gem
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  if defined?(Mail) && Gem.loaded_specs['mail']
         
     | 
| 
      
 26 
     | 
    
         
            +
                    unveil['mail'] = :gem
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                Pledge.unveil(unveil)
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                unless defined?(SimpleCov)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # If running coverage tests, don't run pledged as coverage
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # testing can require many additional permissions.
         
     | 
| 
      
 35 
     | 
    
         
            +
                  Pledge.pledge(pledge)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: unicorn-lockdown
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.0.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Jeremy Evans
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2020-11-09 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: pledge
         
     | 
| 
         @@ -52,10 +52,10 @@ files: 
     | 
|
| 
       52 
52 
     | 
    
         
             
            - bin/unicorn-lockdown-add
         
     | 
| 
       53 
53 
     | 
    
         
             
            - bin/unicorn-lockdown-setup
         
     | 
| 
       54 
54 
     | 
    
         
             
            - files/rc.unicorn
         
     | 
| 
       55 
     | 
    
         
            -
            - lib/chrooter.rb
         
     | 
| 
       56 
55 
     | 
    
         
             
            - lib/rack/email_exceptions.rb
         
     | 
| 
       57 
56 
     | 
    
         
             
            - lib/roda/plugins/pg_disconnect.rb
         
     | 
| 
       58 
57 
     | 
    
         
             
            - lib/unicorn-lockdown.rb
         
     | 
| 
      
 58 
     | 
    
         
            +
            - lib/unveiler.rb
         
     | 
| 
       59 
59 
     | 
    
         
             
            homepage: https://github.com/jeremyevans/unicorn-lockdown
         
     | 
| 
       60 
60 
     | 
    
         
             
            licenses:
         
     | 
| 
       61 
61 
     | 
    
         
             
            - MIT
         
     | 
| 
         @@ -75,9 +75,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       75 
75 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       76 
76 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       77 
77 
     | 
    
         
             
            requirements: []
         
     | 
| 
       78 
     | 
    
         
            -
            rubygems_version: 3. 
     | 
| 
      
 78 
     | 
    
         
            +
            rubygems_version: 3.1.4
         
     | 
| 
       79 
79 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       80 
80 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       81 
     | 
    
         
            -
            summary: Helper library for running Unicorn with  
     | 
| 
       82 
     | 
    
         
            -
              on OpenBSD
         
     | 
| 
      
 81 
     | 
    
         
            +
            summary: Helper library for running Unicorn with fork+exec/unveil/pledge on OpenBSD
         
     | 
| 
       83 
82 
     | 
    
         
             
            test_files: []
         
     | 
    
        data/lib/chrooter.rb
    DELETED
    
    | 
         @@ -1,153 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'etc'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'pledge'
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            # Loading single_byte encoding
         
     | 
| 
       5 
     | 
    
         
            -
            "\255".force_encoding('ISO8859-1').encode('UTF-8')
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
            # Load encodings
         
     | 
| 
       8 
     | 
    
         
            -
            ''.force_encoding('UTF-16LE')
         
     | 
| 
       9 
     | 
    
         
            -
            ''.force_encoding('UTF-16BE')
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
            # Don't run external diff program for failures
         
     | 
| 
       12 
     | 
    
         
            -
            Minitest::Assertions.diff = false
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
            if defined?(SimpleCov) && Process.euid == 0
         
     | 
| 
       15 
     | 
    
         
            -
              # Prevent coverage testing in chroot mode. Chroot vs
         
     | 
| 
       16 
     | 
    
         
            -
              # non-chroot should not matter in terms of lines covered,
         
     | 
| 
       17 
     | 
    
         
            -
              # and running coverage tests in chroot mode will probable fail
         
     | 
| 
       18 
     | 
    
         
            -
              # when it comes time to write the coverage files.
         
     | 
| 
       19 
     | 
    
         
            -
              SimpleCov.at_exit{}
         
     | 
| 
       20 
     | 
    
         
            -
              raise "cannot run coverage testing in chroot mode"
         
     | 
| 
       21 
     | 
    
         
            -
            end
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
            # Chrooter allows for testing programs in both chroot/unveil and non
         
     | 
| 
       24 
     | 
    
         
            -
            # chroot modes.
         
     | 
| 
       25 
     | 
    
         
            -
            module Chrooter
         
     | 
| 
       26 
     | 
    
         
            -
              # If the current user is the super user, drop privileges to
         
     | 
| 
       27 
     | 
    
         
            -
              # the given +user+ first.
         
     | 
| 
       28 
     | 
    
         
            -
              #
         
     | 
| 
       29 
     | 
    
         
            -
              # Use Pledge.unveil to limit access to the file system based on the
         
     | 
| 
       30 
     | 
    
         
            -
              # +unveil+ option.  Then pledge the process with the given +pledge+
         
     | 
| 
       31 
     | 
    
         
            -
              # permissions (if given).
         
     | 
| 
       32 
     | 
    
         
            -
              def self.unveil(user, pledge=nil, unveil={}, group=user)
         
     | 
| 
       33 
     | 
    
         
            -
                require 'unveil'
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                unveil = Hash[unveil]
         
     | 
| 
       36 
     | 
    
         
            -
                if defined?(Rack)
         
     | 
| 
       37 
     | 
    
         
            -
                  unveil['rack'] = :gem
         
     | 
| 
       38 
     | 
    
         
            -
                end
         
     | 
| 
       39 
     | 
    
         
            -
                if defined?(Mail)
         
     | 
| 
       40 
     | 
    
         
            -
                  unveil['mail'] = :gem
         
     | 
| 
       41 
     | 
    
         
            -
                end
         
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
                if Process.euid == 0
         
     | 
| 
       44 
     | 
    
         
            -
                  _drop_privs(user, group)
         
     | 
| 
       45 
     | 
    
         
            -
                  puts "Unveiled, running as user #{user}"
         
     | 
| 
       46 
     | 
    
         
            -
                end
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                Pledge.unveil(unveil)
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                _pledge(pledge)
         
     | 
| 
       51 
     | 
    
         
            -
              end
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
              # If the current user is the super user, change to the given
         
     | 
| 
       54 
     | 
    
         
            -
              # user/group, chroot to the given directory, and pledge
         
     | 
| 
       55 
     | 
    
         
            -
              # the process with the given permissions (if given).
         
     | 
| 
       56 
     | 
    
         
            -
              #
         
     | 
| 
       57 
     | 
    
         
            -
              # If the current user is not the super user, freeze
         
     | 
| 
       58 
     | 
    
         
            -
              # $LOADED_FEATURES to more easily detect problems with
         
     | 
| 
       59 
     | 
    
         
            -
              # autoloaded constants, and just pledge the process with the
         
     | 
| 
       60 
     | 
    
         
            -
              # given permissions (if given).
         
     | 
| 
       61 
     | 
    
         
            -
              #
         
     | 
| 
       62 
     | 
    
         
            -
              # This will reference common autoloaded constants in the
         
     | 
| 
       63 
     | 
    
         
            -
              # rack and mail libraries if they are defined.  Other
         
     | 
| 
       64 
     | 
    
         
            -
              # autoloaded constants should be referenced before calling
         
     | 
| 
       65 
     | 
    
         
            -
              # this method.
         
     | 
| 
       66 
     | 
    
         
            -
              #
         
     | 
| 
       67 
     | 
    
         
            -
              # In general this should be called inside an at_exit block
         
     | 
| 
       68 
     | 
    
         
            -
              # after loading minitest/autorun, so it will run after all
         
     | 
| 
       69 
     | 
    
         
            -
              # specs are loaded, but before running specs.
         
     | 
| 
       70 
     | 
    
         
            -
              def self.chroot(user, pledge=nil, group=user, dir=Dir.pwd)
         
     | 
| 
       71 
     | 
    
         
            -
                # Work around autoload issues in libraries.
         
     | 
| 
       72 
     | 
    
         
            -
                # autoload is problematic when chrooting because if the
         
     | 
| 
       73 
     | 
    
         
            -
                # constant is not referenced before chrooting, an
         
     | 
| 
       74 
     | 
    
         
            -
                # exception is raised if the constant is raised
         
     | 
| 
       75 
     | 
    
         
            -
                # after chrooting.
         
     | 
| 
       76 
     | 
    
         
            -
                #
         
     | 
| 
       77 
     | 
    
         
            -
                # The constants listed here are the autoloaded constants
         
     | 
| 
       78 
     | 
    
         
            -
                # known to be used by any applications.  This list
         
     | 
| 
       79 
     | 
    
         
            -
                # may need to be updated when libraries are upgraded
         
     | 
| 
       80 
     | 
    
         
            -
                # and add new constants, or when applications start
         
     | 
| 
       81 
     | 
    
         
            -
                # using new features.
         
     | 
| 
       82 
     | 
    
         
            -
                if defined?(Rack)
         
     | 
| 
       83 
     | 
    
         
            -
                  Rack::MockRequest if defined?(Rack::MockRequest)
         
     | 
| 
       84 
     | 
    
         
            -
                  Rack::Auth::Digest::Params if defined?(Rack::Auth::Digest::Params)
         
     | 
| 
       85 
     | 
    
         
            -
                  if defined?(Rack::Multipart)
         
     | 
| 
       86 
     | 
    
         
            -
                    Rack::Multipart
         
     | 
| 
       87 
     | 
    
         
            -
                    Rack::Multipart::Parser
         
     | 
| 
       88 
     | 
    
         
            -
                    Rack::Multipart::Generator
         
     | 
| 
       89 
     | 
    
         
            -
                    Rack::Multipart::UploadedFile
         
     | 
| 
       90 
     | 
    
         
            -
                  end
         
     | 
| 
       91 
     | 
    
         
            -
                end
         
     | 
| 
       92 
     | 
    
         
            -
                if defined?(Mail)
         
     | 
| 
       93 
     | 
    
         
            -
                  Mail::Address
         
     | 
| 
       94 
     | 
    
         
            -
                  Mail::AddressList
         
     | 
| 
       95 
     | 
    
         
            -
                  Mail::Parsers::AddressListsParser
         
     | 
| 
       96 
     | 
    
         
            -
                  Mail::ContentTransferEncodingElement
         
     | 
| 
       97 
     | 
    
         
            -
                  Mail::ContentDispositionElement
         
     | 
| 
       98 
     | 
    
         
            -
                  Mail::MessageIdsElement
         
     | 
| 
       99 
     | 
    
         
            -
                  Mail::MimeVersionElement
         
     | 
| 
       100 
     | 
    
         
            -
                  Mail::OptionalField
         
     | 
| 
       101 
     | 
    
         
            -
                  Mail::ContentTypeElement
         
     | 
| 
       102 
     | 
    
         
            -
                end
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                if Process.euid == 0
         
     | 
| 
       105 
     | 
    
         
            -
                  _drop_privs(user, group) do
         
     | 
| 
       106 
     | 
    
         
            -
                    Dir.chroot(dir)
         
     | 
| 
       107 
     | 
    
         
            -
                    Dir.chdir('/')
         
     | 
| 
       108 
     | 
    
         
            -
                  end
         
     | 
| 
       109 
     | 
    
         
            -
                  puts "Chrooted to #{dir}, running as user #{user}"
         
     | 
| 
       110 
     | 
    
         
            -
                else
         
     | 
| 
       111 
     | 
    
         
            -
                  # Load minitest plugins before freezing loaded features,
         
     | 
| 
       112 
     | 
    
         
            -
                  # so they don't break.
         
     | 
| 
       113 
     | 
    
         
            -
                  Minitest.load_plugins
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                  # Emulate chroot not working by freezing $LOADED_FEATURES
         
     | 
| 
       116 
     | 
    
         
            -
                  # This allows to more easily catch bugs that only occur
         
     | 
| 
       117 
     | 
    
         
            -
                  # when chrooted, such as referencing an autoloaded constant
         
     | 
| 
       118 
     | 
    
         
            -
                  # that wasn't loaded before the chroot.
         
     | 
| 
       119 
     | 
    
         
            -
                  $LOADED_FEATURES.freeze
         
     | 
| 
       120 
     | 
    
         
            -
                end
         
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
       122 
     | 
    
         
            -
                _pledge(pledge)
         
     | 
| 
       123 
     | 
    
         
            -
              end
         
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
              def self._drop_privs(user, group)
         
     | 
| 
       126 
     | 
    
         
            -
                uid = Etc.getpwnam(user).uid
         
     | 
| 
       127 
     | 
    
         
            -
                gid = Etc.getgrnam(group).gid
         
     | 
| 
       128 
     | 
    
         
            -
                if Process.egid != gid
         
     | 
| 
       129 
     | 
    
         
            -
                  Process.initgroups(user, gid)
         
     | 
| 
       130 
     | 
    
         
            -
                  Process::GID.change_privilege(gid)
         
     | 
| 
       131 
     | 
    
         
            -
                end
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
                yield if block_given?
         
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
                Process.euid != uid and Process::UID.change_privilege(uid)
         
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
                nil
         
     | 
| 
       138 
     | 
    
         
            -
              end
         
     | 
| 
       139 
     | 
    
         
            -
              private_class_method :_drop_privs
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
              def self._pledge(pledge)
         
     | 
| 
       142 
     | 
    
         
            -
                unless defined?(SimpleCov)
         
     | 
| 
       143 
     | 
    
         
            -
                  if pledge
         
     | 
| 
       144 
     | 
    
         
            -
                    # If running coverage tests, don't run pledged as coverage
         
     | 
| 
       145 
     | 
    
         
            -
                    # testing can require many additional permissions.
         
     | 
| 
       146 
     | 
    
         
            -
                    Pledge.pledge(pledge)
         
     | 
| 
       147 
     | 
    
         
            -
                  end
         
     | 
| 
       148 
     | 
    
         
            -
                end
         
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
                nil
         
     | 
| 
       151 
     | 
    
         
            -
              end
         
     | 
| 
       152 
     | 
    
         
            -
              private_class_method :_pledge
         
     | 
| 
       153 
     | 
    
         
            -
            end
         
     |