diff --git a/services/nginx.spec b/services/nginx.spec index c23b994..e663ce8 100644 --- a/services/nginx.spec +++ b/services/nginx.spec @@ -5,3 +5,7 @@ ports: http=80, https=443 reload-command: kill -HUP ${SERVICE_PID} %configuration nginx.conf + +# They’re defined in the template, be careful to match. +%file access.log +%file error.log diff --git a/shard.yml b/shard.yml index a83520b..c3e87d2 100644 --- a/shard.yml +++ b/shard.yml @@ -14,5 +14,8 @@ dependencies: git: https://git.karchnu.fr/WeirdOS/recipes-parser crinja: github: straight-shoota/crinja + passwd: + git: https://git.karchnu.fr/WeirdOS/passwd.cr + branch: master license: MIT diff --git a/src/gen-config.cr b/src/gen-config.cr index b1fcbe2..7c558b7 100644 --- a/src/gen-config.cr +++ b/src/gen-config.cr @@ -1,5 +1,7 @@ require "crinja" +require "passwd" + require "./service/service.cr" require "./config.cr" @@ -8,7 +10,7 @@ def sanitize_path(path) end class Service - alias CrinjaHash = Hash(String, Hash(String, Int32) | String | Crinja::Callable::Instance | Nil) + alias CrinjaHash = Hash(String, Hash(String, Int32) | String | Crinja::Callable::Instance | Int32 | Nil) def to_genconfig CrinjaHash.new.tap do |entry| entry["name"] = name @@ -18,6 +20,13 @@ class Service entry["domain"] = domain entry["ports"] = ports + user = Passwd.new("/etc/passwd", "/etc/group") + .try &.get_user(user_name).not_nil! + + entry["uid"] = user.uid + entry["gid"] = user.gid + entry["user"] = user.login + entry["consumers"] = Crinja.function do token = arguments.varargs[0].to_s diff --git a/src/service/libc.cr b/src/service/libc.cr index 46b61ae..ceaf263 100644 --- a/src/service/libc.cr +++ b/src/service/libc.cr @@ -25,13 +25,15 @@ module System passwd = pointer.value - r = LibC.setgid passwd.pw_gid + become_user passwd.pw_uid, passwd.pw_uid + end + + def self.become_user(uid, gid) + r = LibC.setgid gid raise Errno.new "setgid" if r != 0 - r = LibC.setuid passwd.pw_uid + r = LibC.setuid uid raise Errno.new "setuid" if r != 0 - - passwd end end #def get_uid_gid(user_name : String) diff --git a/src/service/service.cr b/src/service/service.cr index 7ec544a..ed33b7f 100644 --- a/src/service/service.cr +++ b/src/service/service.cr @@ -2,6 +2,8 @@ require "yaml" require "colorize" require "file_utils" +require "passwd" + require "./context.cr" require "./service_definition.cr" require "./environment.cr" @@ -215,6 +217,7 @@ class Service env["SERVICE_NAME"] = name env["SERVICE_ROOT"] = root env["SERVICE_ID"] = full_id + env["SERVICE_USER"] = user_name if _pid = pid @context.pid_directory env["SERVICE_PID"] = _pid.to_s end @@ -222,11 +225,10 @@ class Service env["ENVIRONMENT"] = @environment.name env["ENVIRONMENT_TYPE"] = @environment.type.to_s - @providers.each do |token, provider| service_provider = @context.get_service_by_id provider - # FIXME: Warning? + # FIXME: Warning if mandatory? next if service_provider.nil? env["#{token.upcase}_PROVIDER"] = provider @@ -294,9 +296,13 @@ class Service context.info "Creating #{file.name}" + uid, gid = get_uid_gid + child = Process.fork do Dir.cd root + System.become_user uid, gid + Process.exec "sh", ["-c", creation_command], output: Process::Redirect::Inherit, error: Process::Redirect::Inherit, @@ -319,7 +325,11 @@ class Service context.title "Starting #{to_s}" end + create_user_and_group! + + uid, gid = get_uid_gid FileUtils.mkdir_p root + File.chown root, uid, gid build_files! context @@ -337,12 +347,7 @@ class Service LibC.dup2 stdout_file.fd, 1 LibC.dup2 stderr_file.fd, 2 - @reference.user.try do |user| - unless System.become_user user - STDERR << "service: child could not setuid() to user '#{user}'.\n" - exit 1 - end - end + System.become_user uid, gid Process.exec command, args, chdir: (@reference.directory.try { |x| evaluate x } || root), @@ -583,7 +588,11 @@ class Service end end + remove_user_and_group! + File.delete "#{context.services_directory}/#{name}.#{@environment.name}.spec" + + FileUtils.rm_rf root end def is_id?(id) @@ -660,6 +669,34 @@ class Service def get_consumers(token) @context.services.select(&.consumes?(token, self)) end + + def user_name + full_id.sub('/', '.') + end + + def get_uid_gid + passwd = Passwd.new("/etc/passwd", "/etc/group") + + user = passwd.get_user(user_name).not_nil! + + {user.uid, user.gid} + end + + def create_user_and_group! + Passwd.new("/etc/passwd", "/etc/group").tap do |passwd| + return if passwd.get_user user_name + + passwd.add_user user_name, + full_name: "Service[#{id}]" + end + end + + def remove_user_and_group! + Passwd.new("/etc/passwd", "/etc/group").tap do |passwd| + passwd.remove_user user_name + passwd.remove_group user_name + end + end end diff --git a/templates/nginx.conf.j2 b/templates/nginx.conf.j2 index b6b0834..12138da 100644 --- a/templates/nginx.conf.j2 +++ b/templates/nginx.conf.j2 @@ -10,8 +10,8 @@ events { } http { - error_log /var/log/{{ service.id | replace("/", "_") }}_error.log warn; - access_log /var/log/{{ service.id | replace("/", "_") }}_access.log; + error_log {{ service.root }}/error.log warn; + access_log {{ service.root }}/access.log; include /etc/nginx/mime.types; default_type application/octet-stream;