Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
nginx-proxy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
nginx-proxy
Commits
8b60f55e
Commit
8b60f55e
authored
Apr 18, 2024
by
nanahira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add auto sign certificate
parent
7ec69b9c
Pipeline
#26557
passed with stages
in 19 minutes and 11 seconds
Changes
6
Pipelines
1
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
853 additions
and
24 deletions
+853
-24
index.ts
index.ts
+1
-0
package-lock.json
package-lock.json
+716
-10
package.json
package.json
+1
-0
src/acme.ts
src/acme.ts
+104
-0
src/check-cert.ts
src/check-cert.ts
+2
-1
src/site.ts
src/site.ts
+29
-13
No files found.
index.ts
View file @
8b60f55e
...
@@ -15,5 +15,6 @@ async function main() {
...
@@ -15,5 +15,6 @@ async function main() {
{
escape
:
(
v
)
=>
v
},
{
escape
:
(
v
)
=>
v
},
),
),
);
);
process
.
exit
(
0
);
}
}
main
();
main
();
package-lock.json
View file @
8b60f55e
This diff is collapsed.
Click to expand it.
package.json
View file @
8b60f55e
...
@@ -56,6 +56,7 @@
...
@@ -56,6 +56,7 @@
"
typescript
"
:
"
^4.7.4
"
"
typescript
"
:
"
^4.7.4
"
},
},
"dependencies"
:
{
"dependencies"
:
{
"
acme-client
"
:
"
^5.3.0
"
,
"
lodash
"
:
"
^4.17.21
"
,
"
lodash
"
:
"
^4.17.21
"
,
"
mustache
"
:
"
^4.2.0
"
,
"
mustache
"
:
"
^4.2.0
"
,
"
nano-md5
"
:
"
^1.0.5
"
"
nano-md5
"
:
"
^1.0.5
"
...
...
src/acme.ts
0 → 100644
View file @
8b60f55e
import
{
createServer
,
Server
}
from
'
http
'
;
import
acme
,
{
Client
}
from
'
acme-client
'
;
import
fs
from
'
fs
'
;
let
email
:
string
;
export
const
domainsToBeSigned
:
string
[]
=
[];
export
function
addSignCert
(
domains
:
string
[],
payload
:
string
)
{
domainsToBeSigned
.
push
(...
domains
);
if
(
!
email
)
{
// acme://
email
=
payload
.
slice
(
7
);
}
return
domainsToBeSigned
[
0
];
}
export
async
function
runSignCert
()
{
if
(
!
domainsToBeSigned
.
length
)
{
return
;
}
console
.
error
(
`Signing certificate for
${
domainsToBeSigned
.
join
(
'
,
'
)}
`
);
// sign a cert with acme-client
const
[
certificateKey
,
certificateRequest
]
=
await
acme
.
crypto
.
createCsr
({
commonName
:
domainsToBeSigned
[
0
],
altNames
:
domainsToBeSigned
.
slice
(
1
),
});
// store as token
const
contentMap
=
new
Map
<
string
,
string
>
();
const
server
=
createServer
(
async
(
req
,
res
)
=>
{
// read token from http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN>
const
token
=
req
.
url
?.
split
(
'
/.well-known/acme-challenge/
'
).
pop
();
if
(
!
token
)
{
// 404
res
.
writeHead
(
404
);
res
.
end
(
'
Token not Found
'
);
return
;
}
const
content
=
contentMap
.
get
(
token
);
if
(
!
content
)
{
// 404
res
.
writeHead
(
404
);
res
.
end
(
'
Content not Found
'
);
return
;
}
res
.
writeHead
(
200
);
res
.
end
(
content
);
}).
listen
(
80
);
await
fs
.
promises
.
mkdir
(
'
/etc/nginx/acme
'
,
{
recursive
:
true
});
let
accountKey
:
Buffer
;
try
{
accountKey
=
await
fs
.
promises
.
readFile
(
'
/etc/nginx/acme/account.pem
'
);
}
catch
(
e
)
{
accountKey
=
await
acme
.
forge
.
createPrivateKey
();
await
fs
.
promises
.
writeFile
(
'
/etc/nginx/acme/account.pem
'
,
accountKey
);
}
const
acmeClient
=
new
Client
({
directoryUrl
:
acme
.
directory
.
letsencrypt
.
production
,
accountKey
,
});
try
{
const
certificate
=
await
acmeClient
.
auto
({
csr
:
certificateRequest
,
email
,
termsOfServiceAgreed
:
true
,
challengePriority
:
[
'
http-01
'
],
challengeCreateFn
:
async
(
authz
,
challenge
,
keyAuthorization
)
=>
{
if
(
challenge
.
type
!==
'
http-01
'
)
{
return
;
}
// store token
console
.
error
(
`Storing token for
${
challenge
.
token
}
:
${
keyAuthorization
}
`
,
);
contentMap
.
set
(
challenge
.
token
,
keyAuthorization
);
},
challengeRemoveFn
:
async
(
authz
,
challenge
,
keyAuthorization
)
=>
{
// does nothing
},
});
console
.
error
(
'
Certificate signed
'
);
// save certificate as fullchain.pem and key as privkey.pem
await
fs
.
promises
.
mkdir
(
`/etc/nginx/certs/
${
domainsToBeSigned
[
0
]}
`
,
{
recursive
:
true
,
});
await
fs
.
promises
.
writeFile
(
`/etc/nginx/certs/
${
domainsToBeSigned
[
0
]}
/fullchain.pem`
,
certificate
,
);
await
fs
.
promises
.
writeFile
(
`/etc/nginx/certs/
${
domainsToBeSigned
[
0
]}
/privkey.pem`
,
certificateKey
,
);
}
catch
(
e
)
{
console
.
error
(
`Failed to sign certificate for
${
domainsToBeSigned
.
join
(
'
,
'
)}
:
${
e
.
stack
}
`
,
);
}
server
.
close
();
}
src/check-cert.ts
View file @
8b60f55e
...
@@ -3,6 +3,7 @@ import * as fs from 'fs';
...
@@ -3,6 +3,7 @@ import * as fs from 'fs';
import
path
from
'
path
'
;
import
path
from
'
path
'
;
import
_
from
'
lodash
'
;
import
_
from
'
lodash
'
;
fs
.
mkdirSync
(
'
/etc/nginx/certs
'
,
{
recursive
:
true
});
class
Cert
{
class
Cert
{
public
cert
=
new
X509Certificate
(
public
cert
=
new
X509Certificate
(
fs
.
readFileSync
(
path
.
join
(
'
/etc/nginx/certs
'
,
this
.
dir
,
'
fullchain.pem
'
)),
fs
.
readFileSync
(
path
.
join
(
'
/etc/nginx/certs
'
,
this
.
dir
,
'
fullchain.pem
'
)),
...
@@ -33,7 +34,7 @@ const certs = fs
...
@@ -33,7 +34,7 @@ const certs = fs
.
map
((
dir
)
=>
new
Cert
(
dir
))
.
map
((
dir
)
=>
new
Cert
(
dir
))
.
filter
((
cert
)
=>
cert
.
isNotExpired
());
.
filter
((
cert
)
=>
cert
.
isNotExpired
());
export
function
pickCert
(
domains
:
string
[])
{
export
async
function
pickCert
(
domains
:
string
[])
{
const
okCerts
=
certs
.
filter
((
cert
)
=>
cert
.
isOkWithDomains
(
domains
));
const
okCerts
=
certs
.
filter
((
cert
)
=>
cert
.
isOkWithDomains
(
domains
));
if
(
!
okCerts
.
length
)
{
if
(
!
okCerts
.
length
)
{
return
;
return
;
...
...
src/site.ts
View file @
8b60f55e
...
@@ -3,6 +3,7 @@ import { Parser } from './parser';
...
@@ -3,6 +3,7 @@ import { Parser } from './parser';
import
{
getSiteNames
}
from
'
./utility
'
;
import
{
getSiteNames
}
from
'
./utility
'
;
import
md5
from
'
nano-md5
'
;
import
md5
from
'
nano-md5
'
;
import
fs
from
'
fs
'
;
import
fs
from
'
fs
'
;
import
{
addSignCert
,
runSignCert
}
from
'
./acme
'
;
export
interface
SiteHttps
{
export
interface
SiteHttps
{
ports
:
string
[];
ports
:
string
[];
...
@@ -100,8 +101,16 @@ function createUpstream(domain: string, urlInputs: string[], extra: string[]) {
...
@@ -100,8 +101,16 @@ function createUpstream(domain: string, urlInputs: string[], extra: string[]) {
return
urlInputs
[
0
];
return
urlInputs
[
0
];
}
}
const
name
=
`upstream_
${
domain
.
replace
(
/
[^
a-zA-Z0-9
]
/g
,
'
_
'
)}
`
;
const
name
=
`upstream_
${
domain
.
replace
(
/
[^
a-zA-Z0-9
]
/g
,
'
_
'
)}
`
;
upstreams
.
push
({
name
,
servers
:
urls
.
map
((
url
)
=>
`
${
url
.
host
+
(
url
.
hash
?
url
.
hash
.
replace
(
/
[
#&
]
/g
,
'
'
)
:
''
)}
`
),
extra
});
upstreams
.
push
({
return
`
${
urls
[
0
].
protocol
}
//
${
name
}${
urlInputs
[
0
].
slice
(
urls
[
0
].
origin
.
length
).
replace
(
urls
[
0
].
hash
,
''
)}
`
;
name
,
servers
:
urls
.
map
(
(
url
)
=>
`
${
url
.
host
+
(
url
.
hash
?
url
.
hash
.
replace
(
/
[
#&
]
/g
,
'
'
)
:
''
)}
`
,
),
extra
,
});
return
`
${
urls
[
0
].
protocol
}
//
${
name
}${
urlInputs
[
0
]
.
slice
(
urls
[
0
].
origin
.
length
)
.
replace
(
urls
[
0
].
hash
,
''
)}
`
;
}
}
async
function
getSiteData
(
async
function
getSiteData
(
...
@@ -115,12 +124,13 @@ async function getSiteData(
...
@@ -115,12 +124,13 @@ async function getSiteData(
const
httpsCert
=
parser
.
getString
(
'
HTTPS
'
);
const
httpsCert
=
parser
.
getString
(
'
HTTPS
'
);
const
domains
=
hostname
.
split
(
'
+
'
);
const
domains
=
hostname
.
split
(
'
+
'
);
if
(
httpsCert
!==
'
0
'
&&
httpsCert
!==
'
false
'
)
{
if
(
httpsCert
!==
'
0
'
&&
httpsCert
!==
'
false
'
)
{
const
cert
=
const
cert
=
httpsCert
?.
startsWith
(
'
acme://
'
)
!
httpsCert
||
?
addSignCert
(
domains
,
httpsCert
)
:
!
httpsCert
||
httpsCert
===
'
1
'
||
httpsCert
===
'
1
'
||
httpsCert
===
'
true
'
||
httpsCert
===
'
true
'
||
httpsCert
===
'
auto
'
httpsCert
===
'
auto
'
?
pickCert
(
domains
)
?
await
pickCert
(
domains
)
:
httpsCert
;
:
httpsCert
;
if
(
cert
)
{
if
(
cert
)
{
if
(
port
)
{
if
(
port
)
{
...
@@ -167,7 +177,11 @@ async function getSiteData(
...
@@ -167,7 +177,11 @@ async function getSiteData(
const
sni
=
parser
.
getString
(
'
SNI
'
);
const
sni
=
parser
.
getString
(
'
SNI
'
);
specificRenderData
=
{
specificRenderData
=
{
proxy
:
true
,
proxy
:
true
,
upstream
:
createUpstream
(
domain
,
targetUrlInputs
,
parser
.
getArray
(
'
UPSTREAM_EXTRA
'
)),
upstream
:
createUpstream
(
domain
,
targetUrlInputs
,
parser
.
getArray
(
'
UPSTREAM_EXTRA
'
),
),
noVerifyCerts
:
parser
.
getBoolean
(
'
NO_VERIFY_CERTS
'
),
noVerifyCerts
:
parser
.
getBoolean
(
'
NO_VERIFY_CERTS
'
),
noBuffer
:
parser
.
getBoolean
(
'
NO_BUFFER
'
),
noBuffer
:
parser
.
getBoolean
(
'
NO_BUFFER
'
),
sni
:
sni
===
'
1
'
,
sni
:
sni
===
'
1
'
,
...
@@ -214,6 +228,10 @@ export async function getData(
...
@@ -214,6 +228,10 @@ export async function getData(
input
:
Record
<
string
,
string
>
=
process
.
env
,
input
:
Record
<
string
,
string
>
=
process
.
env
,
):
Promise
<
RenderData
>
{
):
Promise
<
RenderData
>
{
const
parser
=
new
Parser
(
''
,
input
);
const
parser
=
new
Parser
(
''
,
input
);
const
sites
=
await
Promise
.
all
(
getSiteNames
().
map
((
domain
)
=>
getSiteData
(
domain
,
input
)),
);
await
runSignCert
();
return
{
return
{
purgeAllowed
:
parser
.
getArray
(
'
PURGE_ALLOWED
'
),
purgeAllowed
:
parser
.
getArray
(
'
PURGE_ALLOWED
'
),
externalRealIp
:
parser
.
getBoolean
(
'
EXTERNAL_REAL_IP
'
),
externalRealIp
:
parser
.
getBoolean
(
'
EXTERNAL_REAL_IP
'
),
...
@@ -226,9 +244,7 @@ export async function getData(
...
@@ -226,9 +244,7 @@ export async function getData(
ticketKeyPath
:
ticketKeyPath
:
parser
.
getString
(
'
TICKET_KEY_PATH
'
)
||
'
/etc/nginx/generated/ticket.key
'
,
parser
.
getString
(
'
TICKET_KEY_PATH
'
)
||
'
/etc/nginx/generated/ticket.key
'
,
certsPath
:
parser
.
getString
(
'
CERTS_PATH
'
)
||
'
/etc/nginx/certs
'
,
certsPath
:
parser
.
getString
(
'
CERTS_PATH
'
)
||
'
/etc/nginx/certs
'
,
sites
:
await
Promise
.
all
(
sites
,
getSiteNames
().
map
((
domain
)
=>
getSiteData
(
domain
,
input
)),
),
upstreams
,
upstreams
,
httpExtra
:
parser
.
getArray
(
'
HTTP_EXTRA
'
),
httpExtra
:
parser
.
getArray
(
'
HTTP_EXTRA
'
),
nginxExtra
:
parser
.
getArray
(
'
NGINX_EXTRA
'
),
nginxExtra
:
parser
.
getArray
(
'
NGINX_EXTRA
'
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment