blob: 1ce022dabd510a1bdb5f109e8b3ac12c8fbaa689 [file] [log] [blame]
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +02001# Deploy a Docker Registry in a cluster.
2
3# This needs an oauth2 secret provisioned, create with:
4# kubectl -n registry create secret generic auth --from-literal=oauth2_secret=...
5# kubectl get secrets rook-ceph-object-user-<ceph-pool>-object-registry -n <ceph-namespace> -o yaml --export | kubectl replace -f - -n registry
6
7local kube = import "../../../kube/kube.libsonnet";
8local cm = import "cert-manager.libsonnet";
9
10{
11 Environment: {
12 local env = self,
13 local cfg = env.cfg,
14 cfg:: {
15 namespace: "registry",
16 domain: error "domain must be set",
17 storageClassName: error "storageClassName must be set",
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +020018 objectStoreName: error "objectStoreName must be set",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +020019 },
20
21 metadata(component):: {
22 namespace: cfg.namespace,
23 labels: {
24 "app.kubernetes.io/name": "registry",
25 "app.kubernetes.io/managed-by": "kubecfg",
26 "app.kubernetes.io/component": component,
27 },
28 },
29
30 namespace: kube.Namespace(cfg.namespace),
31
32 registryIssuer: cm.Issuer("registry-issuer") {
33 metadata+: env.metadata("registry-issuer"),
34 spec: {
35 selfSigned: {},
36 },
37 },
38 authCertificate: cm.Certificate("auth") {
39 metadata+: env.metadata("auth"),
40 spec: {
41 secretName: "auth-internal",
42 duration: "43800h0m0s", // 5 years
43 issuerRef: {
44 name: env.registryIssuer.metadata.name,
45 },
46 commonName: "auth.registry",
47 },
48 },
49 registryCertificate: cm.Certificate("registry") {
50 metadata+: env.metadata("registry"),
51 spec: {
52 secretName: "registry-internal",
53 duration: "43800h0m0s", // 5 years
54 issuerRef: {
55 name: env.registryIssuer.metadata.name,
56 },
57 commonName: "registry.registry",
58 },
59 },
60
61 registryConfig: kube.ConfigMap("registry-config") {
62 metadata+: env.metadata("registry-config"),
63 data: {
64 "config.yml": std.manifestYamlDoc({
65 version: "0.1",
66 log: {
67 fields: {
68 service: "registry",
69 },
70 },
71 storage: {
72 cache: {
73 blobdescriptor: "inmemory",
74 },
75 s3: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +020076 regionendpoint: "https://object.ceph-waw2.hswaw.net",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +020077 bucket: "registry",
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +020078 region: "waw-hdd-redunant-2-object:default-placement",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +020079 },
80 },
81 http: {
82 addr: ":5000",
83 headers: {
84 "X-Content-Type-Options": ["nosniff"],
85 },
86 tls: {
87 certificate: "/certs/tls.crt",
88 key: "/certs/tls.key",
89 },
90 debug: {
91 addr: "localhost:5001",
92 },
93 },
94 health: {
95 storagedriver: {
96 enabled: true,
97 interval: "10s",
98 threshold: 3,
99 },
100 },
101 auth: {
102 token: {
103 realm: "https://%s/auth" % [cfg.domain],
104 service: "my.docker.registry",
105 issuer: "%s auth server" % [cfg.domain],
106 rootcertbundle: "/authcerts/tls.crt",
107 },
108 },
109 }),
110 },
111 },
112
113 authVolumeClaim: kube.PersistentVolumeClaim("auth-token-storage") {
114 metadata+: env.metadata("auth-token-storage"),
115 spec+: {
116 storageClassName: cfg.storageClassName,
117 accessModes: [ "ReadWriteOnce" ],
118 resources: {
119 requests: {
120 storage: "1Gi",
121 },
122 },
123 },
124 },
125
126 authConfig: kube.ConfigMap("auth-config") {
127 metadata+: env.metadata("auth-config"),
128 data: {
129 "auth_config.yml": std.manifestYamlDoc({
130 server: {
131 addr: ":5001",
132 certificate: "/certs/tls.crt",
133 key: "/certs/tls.key",
134 },
135 token: {
136 issuer: "%s auth server" % [cfg.domain],
137 expiration: 900,
138 },
139 oauth2: {
140 client_id: "registry",
141 client_secret_file: "/secrets/oauth2_secret",
142 authorize_url: "https://sso.hackerspace.pl/oauth/authorize",
143 access_token_url: "https://sso.hackerspace.pl/oauth/token",
144 profile_url: "https://sso.hackerspace.pl/api/1/profile",
145 redirect_url: "https://registry.k0.hswaw.net/oauth2",
146 username_key: "username",
147 token_db: "/data/oauth2_tokens.ldb",
148 registry_url: "https://registry.k0.hswaw.net",
149 },
150 users: {
151 [""]: {}, // '' user are anonymous users.
152 },
153 local data = self,
154 pushers:: [
155 { who: ["q3k", "inf"], what: "vms/*" },
156 { who: ["q3k", "inf"], what: "app/*" },
157 { who: ["q3k", "inf"], what: "go/svc/*" },
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200158 { who: ["q3k"], what: "bgpwtf/*" },
159 { who: ["q3k"], what: "devtools/*" },
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200160 ],
161 acl: [
162 {
163 match: {account: "/.+/", name: "${account}/*"},
164 actions: ["*"],
165 comment: "Logged in users have full access to images that are in their 'namespace'",
166 },
167 {
168 match: {account: "/.+/", type: "registry", name: "catalog"},
169 actions: ["*"],
170 comment: "Logged in users can query the catalog.",
171 },
172 {
173 match: {account: ""},
174 actions: ["pull"],
175 comment: "Anyone can pull all images.",
176 },
177 ] + [
178 {
179 match: {
180 account: "/(%s)/" % std.join("|", p.who),
181 name: p.what,
182 },
183 actions: ["*"],
184 comment: "%s can push to %s" % [std.join(", ", p.who), p.what],
185 }
186 for p in data.pushers
187 ],
188 }),
189 }
190 },
191
192 authDeployment: kube.Deployment("auth") {
193 metadata+: env.metadata("auth"),
194 spec+: {
195 replicas: 1,
196 template+: {
197 spec+: {
198 volumes_: {
199 data: kube.PersistentVolumeClaimVolume(env.authVolumeClaim),
200 config: kube.ConfigMapVolume(env.authConfig),
201 certs: {
202 secret: { secretName: env.authCertificate.spec.secretName },
203 },
204 secrets: {
205 secret: { secretName: "auth" },
206 },
207 },
208 containers_: {
209 auth: kube.Container("auth") {
210 image: "informatic/docker_auth:2019040307",
211 volumeMounts_: {
212 config: { mountPath: "/config" },
213 certs: { mountPath: "/certs" },
214 secrets: { mountPath: "/secrets" },
215 data: { mountPath: "/data" },
216 },
217 },
218 },
219 },
220 },
221 },
222 },
223 authService: kube.Service("auth") {
224 metadata+: env.metadata("auth"),
225 target_pod:: env.authDeployment.spec.template,
226 spec+: {
227 type: "ClusterIP",
228 ports: [
229 { name: "auth", port: 5001, targetPort: 5001, protocol: "TCP" },
230 ],
231 }
232 },
233 registryDeployment: kube.Deployment("docker-registry") {
234 metadata+: env.metadata("docker-registry"),
235 spec+: {
236 replicas: 1,
237 template+: {
238 spec+: {
239 volumes_: {
240 config: kube.ConfigMapVolume(env.registryConfig),
241 certs: {
242 secret: { secretName: env.registryCertificate.spec.secretName },
243 },
244 authcerts: {
245 secret: { secretName: env.authCertificate.spec.secretName },
246 },
247 },
248 containers_: {
249 registry: kube.Container("docker-registry") {
250 image: "registry:2",
251 args: ["/config/config.yml"],
252 volumeMounts_: {
253 config: { mountPath: "/config" },
254 certs: { mountPath: "/certs" },
255 authcerts: { mountPath: "/authcerts" },
256 },
257 env_: {
258 REGISTRY_STORAGE_S3_ACCESSKEY: { secretKeyRef: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200259 name: "rook-ceph-object-user-%(objectStorageName)s-registry" % {objectStorageName: cfg.objectStorageName},
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200260 key: "AccessKey"
261 }},
262 REGISTRY_STORAGE_S3_SECRETKEY: { secretKeyRef: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200263 name: "rook-ceph-object-user-%(objectStorageName)s-registry" % {objectStorageName: cfg.objectStorageName},
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200264 key: "SecretKey",
265 }},
266 },
267 },
268 },
269 },
270 },
271 },
272 },
273 registryService: kube.Service("docker-registry") {
274 metadata+: env.metadata("docker-registry"),
275 target_pod:: env.registryDeployment.spec.template,
276 spec+: {
277 type: "ClusterIP",
278 ports: [
279 { name: "registry", port: 5000, targetPort: 5000, protocol: "TCP" },
280 ],
281 }
282 },
283 registryIngress: kube.Ingress("registry") {
284 metadata+: env.metadata("registry") {
285 annotations+: {
286 "kubernetes.io/tls-acme": "true",
287 "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
288 "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS",
289 "nginx.ingress.kubernetes.io/proxy-body-size": "0",
290 },
291 },
292 spec+: {
293 tls: [
294 {
295 hosts: [cfg.domain],
296 secretName: "registry-tls",
297 },
298 ],
299 rules: [
300 {
301 host: cfg.domain,
302 http: {
303 paths: [
304 { path: "/auth", backend: env.authService.name_port },
305 { path: "/", backend: env.authService.name_port },
306 { path: "/v2/", backend: env.registryService.name_port },
307 ]
308 },
309 }
310 ],
311 },
312 },
313
314 registryStorageUser: kube._Object("ceph.rook.io/v1", "CephObjectStoreUser", "registry") {
315 metadata+: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200316 namespace: "ceph-waw2",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200317 },
318 spec: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200319 store: cfg.objectStorageName,
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200320 displayName: "docker-registry user",
321 },
322 },
323 }
324}