feat: moved services from source to here, still untested: vsftpd,
mongodb, mysql, nginx
This commit is contained in:
24
services/nginx/serverman-service-nginx.cabal
Normal file
24
services/nginx/serverman-service-nginx.cabal
Normal file
@ -0,0 +1,24 @@
|
||||
name: serverman-service-nginx
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
homepage: https://github.com/githubuser/nginx#readme
|
||||
license: GPL-3
|
||||
license-file: LICENSE
|
||||
author: Author name here
|
||||
maintainer: example@example.com
|
||||
copyright: 2017 Author name here
|
||||
category: Web
|
||||
build-type: Simple
|
||||
cabal-version: >=1.10
|
||||
extra-source-files: README.md
|
||||
|
||||
executable nginx
|
||||
hs-source-dirs: src
|
||||
main-is: Main.hs
|
||||
default-language: Haskell2010
|
||||
build-depends: base >= 4.7 && < 5
|
||||
, data-default-class
|
||||
, monad-control
|
||||
, mtl
|
||||
, free
|
BIN
services/nginx/src/Main
Executable file
BIN
services/nginx/src/Main
Executable file
Binary file not shown.
BIN
services/nginx/src/Main.hi
Normal file
BIN
services/nginx/src/Main.hi
Normal file
Binary file not shown.
126
services/nginx/src/Main.hs
Normal file
126
services/nginx/src/Main.hs
Normal file
@ -0,0 +1,126 @@
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
module Main (call, main) where
|
||||
import System.Serverman.Types
|
||||
import System.Serverman.Utils
|
||||
import Types
|
||||
|
||||
import System.Directory
|
||||
import System.IO
|
||||
import System.IO.Error
|
||||
import System.FilePath
|
||||
import System.Process
|
||||
import Control.Concurrent.Async
|
||||
import Control.Monad
|
||||
import Control.Monad.State
|
||||
import Control.Monad.Free
|
||||
import Data.List
|
||||
|
||||
main :: IO ()
|
||||
main = return ()
|
||||
|
||||
call :: Service -> App ()
|
||||
call _ =
|
||||
do
|
||||
(AppState { arguments }) <- get
|
||||
let params@(ServerParams { ssl, domain, directory, serverType, email }) = toServerParams arguments
|
||||
|
||||
-- Turn SSL off at first, because we have not yet received a certificate
|
||||
let content = show (params { ssl = False, port = "80" })
|
||||
config = "/etc/nginx/"
|
||||
mainConfig = "/etc/nginx/nginx.conf"
|
||||
parent = config </> "serverman-configs"
|
||||
path = parent </> domain
|
||||
targetDir = directory
|
||||
|
||||
createCert path cmd = do
|
||||
result <- executeRoot cmd ["certonly", "--webroot", "--webroot-path", directory, "-d", domain, "--email", email, "--agree-tos", "-n"] "" False
|
||||
case result of
|
||||
Left _ -> if cmd == "letsencrypt" then createCert path "certbot" else return ()
|
||||
Right stdout -> do
|
||||
liftIO $ putStrLn stdout
|
||||
|
||||
when (not ("error" `isInfixOf` stdout)) $ do
|
||||
liftIO $ writeFile path (show params)
|
||||
liftIO . wait =<< restart
|
||||
return ()
|
||||
|
||||
liftIO $ do
|
||||
createDirectoryIfMissing True targetDir
|
||||
createDirectoryIfMissing True parent
|
||||
|
||||
writeIncludeStatementIfMissing mainConfig parent
|
||||
|
||||
when ssl $ do
|
||||
let sslPath = config </> "ssl.conf"
|
||||
writeFileIfMissing sslPath nginxSSL
|
||||
putStrLn $ "wrote ssl configuration to " ++ sslPath
|
||||
|
||||
writeFile path content
|
||||
|
||||
putStrLn $ "wrote your configuration file to " ++ path
|
||||
|
||||
liftIO . wait =<< restart
|
||||
|
||||
when ssl $ do
|
||||
let dhparamPath = "/etc/ssl/certs/dhparam.pem"
|
||||
dhExists <- liftIO $ doesFileExist dhparamPath
|
||||
|
||||
when (not dhExists) $ do
|
||||
dhparam <- liftedAsync $ executeRoot "openssl" ["dhparam", "-out", dhparamPath, "2048"] "" True
|
||||
liftIO $ wait dhparam
|
||||
return ()
|
||||
|
||||
case serverType of
|
||||
Static -> do
|
||||
letsencrypt <- liftedAsync $ createCert path "letsencrypt"
|
||||
|
||||
liftIO $ wait letsencrypt
|
||||
return ()
|
||||
_ -> liftIO $ do
|
||||
putStrLn $ "you should use letsencrypt to create a certificate for your domain"
|
||||
putStrLn $ "and put it in /etc/letsencrypt/live/" ++ domain ++ "/fullchain.pem"
|
||||
putStrLn $ "my suggestion is running this command:"
|
||||
putStrLn $ "sudo letsencrypt certonly --webroot --webroot-path <YOUR_APPLICATION_DIRECTORY> -d " ++ domain
|
||||
|
||||
liftIO $ putStrLn $ "for more information, see: https://certbot.eff.org/"
|
||||
|
||||
return ()
|
||||
where
|
||||
restart = liftedAsync $ do
|
||||
result <- restartService "nginx"
|
||||
case result of
|
||||
Left err -> return ()
|
||||
Right _ ->
|
||||
liftIO $ putStrLn $ "restarted nginx"
|
||||
|
||||
writeIncludeStatementIfMissing path target = do
|
||||
content <- readFile path
|
||||
|
||||
let statement = "include " ++ target ++ "/*;"
|
||||
|
||||
when (not (statement `isInfixOf` content)) $ do
|
||||
let newContent = appendAfter content "http {" (indent . indent $ statement)
|
||||
|
||||
writeFile path newContent
|
||||
|
||||
nginxSSL = "# from https://cipherli.st/\n\
|
||||
\# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html\n\
|
||||
\\n\
|
||||
\ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n\
|
||||
\ssl_prefer_server_ciphers on;\n\
|
||||
\ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';\n\
|
||||
\ssl_ecdh_curve secp384r1;\n\
|
||||
\ssl_session_cache shared:SSL:10m;\n\
|
||||
\ssl_session_tickets off;\n\
|
||||
\ssl_stapling on;\n\
|
||||
\ssl_stapling_verify on;\n\
|
||||
\resolver 8.8.8.8 8.8.4.4 valid=300s;\n\
|
||||
\resolver_timeout 5s;\n\
|
||||
\# Disable preloading HSTS for now. You can use the commented out header line that includes\n\
|
||||
\# the 'preload' directive if you understand the implications.\n\
|
||||
\#add_header Strict-Transport-Security 'max-age=63072000; includeSubdomains; preload';\n\
|
||||
\add_header Strict-Transport-Security 'max-age=63072000; includeSubdomains';\n\
|
||||
\add_header X-Frame-Options DENY;\n\
|
||||
\add_header X-Content-Type-Options nosniff;\n\
|
||||
\\n\
|
||||
\ssl_dhparam /etc/ssl/certs/dhparam.pem;\n"
|
BIN
services/nginx/src/Main.o
Normal file
BIN
services/nginx/src/Main.o
Normal file
Binary file not shown.
76
services/nginx/src/Types.hs
Normal file
76
services/nginx/src/Types.hs
Normal file
@ -0,0 +1,76 @@
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
module Types ( ServerType (..)
|
||||
, ServerParams (..)
|
||||
, toServerParams) where
|
||||
|
||||
import System.Serverman.Types
|
||||
import System.Serverman.Utils
|
||||
|
||||
import Data.Default.Class
|
||||
|
||||
toServerParams :: [(String, Maybe String)] -> ServerParams
|
||||
toServerParams (("directory", Just value):xs) = (toServerParams xs) { directory = value, serverType = Static }
|
||||
toServerParams (("domain", Just value):xs) = (toServerParams xs) { domain = value }
|
||||
toServerParams (("port", Just value):xs) = (toServerParams xs) { port = value }
|
||||
toServerParams (("forward", Just value):xs) = (toServerParams xs) { forward = value, serverType = PortForwarding }
|
||||
toServerParams (("email", Just value):xs) = (toServerParams xs) { email = value }
|
||||
toServerParams (("ssl", Nothing):xs) = (toServerParams xs) { ssl = True }
|
||||
toServerParams _ = def
|
||||
|
||||
data ServerType = Static | PortForwarding deriving (Show, Eq)
|
||||
data ServerParams = ServerParams { directory :: FilePath
|
||||
, domain :: String
|
||||
, port :: String
|
||||
, forward :: String
|
||||
, email :: String
|
||||
, ssl :: Bool
|
||||
, serverType :: ServerType
|
||||
} deriving (Eq)
|
||||
|
||||
instance Default ServerParams where
|
||||
def = ServerParams { directory = "/var/www"
|
||||
, domain = "localhost"
|
||||
, port = "80"
|
||||
, forward = ""
|
||||
, email = ""
|
||||
, ssl = False
|
||||
, serverType = Static }
|
||||
|
||||
instance Show ServerParams where
|
||||
show (ServerParams { directory, domain, port, forward, email, ssl, serverType }) =
|
||||
let redirect
|
||||
| ssl = block "server" $
|
||||
semicolon $
|
||||
keyvalue ([ ("listen", "80")
|
||||
, ("listen", "[::]:80")
|
||||
, ("server_name", domain)
|
||||
, ("rewrite", "^ https://$server_name$request_uri? permanent")
|
||||
]) " "
|
||||
| otherwise = ""
|
||||
https
|
||||
| ssl = [ ("ssl_certificate", "/etc/letsencrypt/live/" ++ domain ++ "/fullchain.pem")
|
||||
, ("ssl_certificate_key", "/etc/letsencrypt/live/" ++ domain ++ "/privkey.pem")
|
||||
, ("include", "ssl.conf")]
|
||||
| otherwise = []
|
||||
|
||||
listen = port ++ (if ssl then " ssl" else "")
|
||||
|
||||
base = [ ("server_name", domain)
|
||||
, ("listen", listen)
|
||||
, ("listen", "[::]:" ++ listen)
|
||||
, ("index", "index.html index.html index.php")
|
||||
] ++ https
|
||||
in
|
||||
case serverType of
|
||||
Static ->
|
||||
(block "server" $ keyvalue (base ++ [("root", directory)]) " ") ++ "\n" ++ redirect
|
||||
|
||||
PortForwarding ->
|
||||
let proxyBlock = block "location /" $
|
||||
semicolon $
|
||||
keyvalue ([ ("proxy_pass", "http://127.0.0.1:" ++ forward)
|
||||
, ("proxy_set_header", "X-Forwarded-Host $host")
|
||||
, ("proxy_set_header", "X-Forwarded-Server $host")
|
||||
, ("proxy_set_header", "X-Forwarded-For $proxy_add_x_forwarded_for")
|
||||
]) " "
|
||||
in (block "server" $ semicolon (keyvalue base " ") ++ proxyBlock) ++ "\n" ++ redirect
|
66
services/nginx/stack.yaml
Normal file
66
services/nginx/stack.yaml
Normal file
@ -0,0 +1,66 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# http://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# resolver: lts-3.5
|
||||
# resolver: nightly-2015-09-21
|
||||
# resolver: ghc-7.10.2
|
||||
# resolver: ghcjs-0.1.0_ghc-7.10.2
|
||||
# resolver:
|
||||
# name: custom-snapshot
|
||||
# location: "./custom-snapshot.yaml"
|
||||
resolver: lts-8.5
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# - location:
|
||||
# git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
# extra-dep: true
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
#
|
||||
# A package marked 'extra-dep: true' will only be built if demanded by a
|
||||
# non-dependency (i.e. a user package), and its test suites and benchmarks
|
||||
# will not be run. This is useful for tweaking upstream packages.
|
||||
packages:
|
||||
- '.'
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver
|
||||
# (e.g., acme-missiles-0.3)
|
||||
extra-deps: []
|
||||
|
||||
# Override default flag values for local packages and extra-deps
|
||||
flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=1.1"
|
||||
#
|
||||
# Override the architecture used by stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
Reference in New Issue
Block a user